Should the internal classes be unit tested
Yes, definitely!
Let's take a step back. Why do we spend time
writing automated unit tests? To save us time
in future. Inevitably there will be a new feature,
a bugfix, a refactor. And after the dust settles
and the code's committed, we will want to verify
that stuff still works, with no regressions.
Minimally, we have to at least do that for the
happy path.
Cardinal rule: A line of code that never ran
is probably a line that is buggy.
So we want to measure
code coverage
and write more tests until we believe maintenance risk
has been adequately mitigated.
I don't particularly care whether we call an automated
test "unit" or "integration", at least not initially.
My chief concern is to have a test exercise the code,
so Red source lines in the coverage report turn Green.
A test that comes straight from a User Story is likely
to be a relatively high level integration test,
and it might suffice to adequately mitigate risk.
With some testing in place, it's time to look at
the uncovered Red lines and ask how we might exercise them.
Perhaps there's an if
looking for two values
and only a single input value has been provided so far.
Great! Add a tweaked test case which exercises the else
,
verify the line turned Green, and move on.
But sometimes a conditional or exception is "impossible"
to exercise, it is a "cannot happen" case, at least after
functions higher on the call stack have sanitized inputs.
In those cases, definitely write an isolated unit test
which exercises all the cases.
Also prefer unit tests for functions with multiple callers,
in case requirements for one of the callers changes in future.
If you ever find that it's non-trivial to figure out
why a big integration test failed or how to fix it, definitely
break out simple unit tests.
If an integration test takes a "long" time,
again it's time to break out unit tests
or rely on test double techniques such as mocking.
If a source line stubbornly remains Red,
and is "hard" to cover, consider whether it is even
needed in production. Something that is never called
might be something we can safely delete.
Now, back to your original question.
Write an automated test that directly corresponds to
a User Story. Likely it is a high level integration test.
Examine the coverage report, and write additional
integration or unit tests as warranted. Lather, rinse, repeat.
At some point you will see some uncovered code that you
want to access but it is "hard", perhaps because it is private.
In a language like python you should not hesitate for a moment,
just call the private _helper()
-- that's a valuable test.
In a language that prohibits such access,
you'll have some choices to make.
Your test might need to be a friend, in the same module,
or otherwise close enough to the target code to allow access.
You might promote the target method, or add a public method,
solely to support testing.
You might reconsider your public API,
for example by adopting Dependency Injection.
There are many possibilities, but at the end
of the day you need an automated test suite
that brings maintenance risk down to what
you find is an acceptable level.
So yes, test the internal classes, one way or another.
theOnlyPublicClass.instantiateInternalClasses();
and then what? If nothing goes in and nothing comes out there is nothing to test since this does nothing.