Question: Advice on unit testing is often focused on publicly exposed classes. What about cases where the only public class is simply there to instantiate internal classes? Should the internal classes be unit tested, even if they are not exposed to the outside world?

My intuition tells me that the internal classes are doing the meaningful work and should be tested, regardless of the semantics.

Answer: 
> 
> Should the internal classes be unit tested, even if they are not exposed to the outside world?
> 
> 
> 

* (a) Do we make mistakes when implementing internal classes; especially mistakes that risk significant loss of property?
* (b) Is is acceptable to ship implementations of internal classes that risk significant loss of property?

When the answers to these questions are commonly (a) yes and (b) no; we will usually want to make *some* investment in risk mitigation.

*Which* investment(s) we want to make, and whether or not "unit testing" is an appropriate label for some subset of those investments -- that's a messier question.

Code that is complicated and changing often certainly needs some cost-effective mistake detection strategy; fast/cheap automated checks that assist the programmer in verifying their changes is likely to be a good idea to try.
I think any possible mixture of opinions here might stem from how we perceive "public" vs. "internal". In my particular case, "public" encompasses a very narrow subset of our software development kit's functionality and "internal" a massive superset of functionality - including very generalized functionality used from various public functions and types -- so it's absolutely crucial that we write unit tests (as well as integration tests) for the "internal" functions and types.

Of course if all you have is an internal "helper" class used in one place -- and perhaps even guaranteed to stay this way (ex: a nested private class like a node for a tree as a basic CS example) -- then I think it's productive only to test the public interface of the public class. There you have a distinction not just between "public" and "internal" but between "interface" and "implementation detail". It can quickly work towards counter-productivity to try to handle edge cases that can never be encountered in implementation details through the constraints of the way that such internal functionality is guaranteed to be used by only one place.

Especially for large-scale designs, you generally can't afford to perfect every implementation detail to handle every possible use case that's a massive superset of the actual use case needed of the public interface. What's most important in a large-scale system is to make sure your interfaces consistently produce expected outputs for a variety of given inputs (as wide a variety as needed to test every unique branch of code). It can also go against the idea that implementation details should be free to change without breaking any external dependencies, since changing the implementation details might break those internal unit tests even when the interface still conforms to the identical input and output requirements. So I'd generally avoid unit testing anything that can be considered an implementation detail to keep them more free and flexible to change.

The perfectionist in me usually wants to handle all those edge cases that can never presently manifest even in implementation details guaranteed only to be used by one place but the need for productivity often calls for YAGNI (I compromise by sprinkling lots of asserts to make sure the internal implementation detail/helper class isn't used in a way it isn't supposed to by the public class). If the implementation details can't handle some hypothetical use case that will never manifest given the constraints of the public classes and the way they use those implementation details, then I think it can only be argued from a business standpoint that we're wasting time if we're trying to test for a problem that will never (or unlikely to ever) manifest.

*BTW: Apologies if this is a dumb answer. I wanted to write it in a comment but I lack the rep.*