I'm asking about C#, but this probably applies to most other languages as well.

Imagine I have a project with a lot of complex logic, split up into a lot of small components. Let's say that, among other things, it contains a ComplexProjectFacade with a single method accepting some parameters and returning some value - and this is the only component that is supposed to be public.

But the logic performed within what is called from that single method is very complex and testing directly on the ComplexProjectFacade gives not nearly enough certainty that all works correctly. What should I do in this case?

Put the unit tests in the same project?

Make other components publicly accessible only for the sake of testing?

Or maybe it is already wrong to make the components internal? Maybe they should be accessible from outside?

Is the problem that you can't generate enough permutations to the public method to test all paths?
– Robbie DeeMar 3 '16 at 8:50

1

That's one thing. But also there might be two bugs whose cumulative effect is actual correct behavior (but perhaps not in all cases). Also, if I only test the single public component, if the test fails - I have no idea where to look for the error. I realize unit tests are supposed to test public behavior, but this is a "what-if" scenario where there is a lot of encapsulated logic.
– user622505Mar 3 '16 at 8:52

1

Well you can use things like [assembly: InternalsVisibleTo()] to expose your internal methods but it sounds like you're wanting to test the innards as they're not providing enough information when they go over. I'd concentrate on resolving that as a first port of call.
– Robbie DeeMar 3 '16 at 8:56

2

This is a specific case where there are a limited number of exposed methods with complexity underneath. It most certainly isn't a vanilla question about whether internal/private methods should be tested.
– Robbie DeeMar 3 '16 at 10:49

1

Duplicate question seems to be weighing the pros and cons of performing unit tests on internal components, while this question appears to be asking how to unit test internal components. I am voting to reopen.
– RachelMar 10 '16 at 21:20

1 Answer
1

Extract your encapsulated logic into smaller classes. Keep doing this until you get classes that adhere to the SRP principle and have one, and only one responsibility. Write tests on those classes to test that they perform the little responsibility that they have correctly. Compose your bigger classes from these small classes, until you have the one ComplexProjectFacade which is composed from smaller classes, and so on.

By doing this, you create small, focused and easy-to-understand classes. The fact that they have methods that are public and can therefore be tested is a happy side effect from the fact that you now have code that is easy to understand. Whenever I hear somebody argue about having to make classes or methods public purely for the sake of testing, I try to make them realize that they are also more clearly adhering to principles that lead to better code (e.g. SOLID).

How you do beyond this is more up to personal preference:

Putting the tests next to the code in the same assembly pollutes your released code with the tests. They will be in the same DLL which increases size, and so on. You probably do not want to release your compiled test code with your application but if you have no qualms with that, it's a valid approach. Still requires you to have every class marked as Internal

Keeping the classes public means that the exposed interface of your DLL becomes more complex. Users wanting to use your DLL will see a wide range of classes and it might not be immediately obvious what class to use to perform something. Proper usage of namespaces might prevent this somewhat as you can limit the root namespace to the facade and putting all the other classes into separate (sub-)namespaces, making it more obvious what your users need to look at.

InternalsVisibleTo allows you to keep all your small classes internal to the assembly, limiting the exposed interface of your DLL to things you really intended to have exposed, but requires you to keep all the classes internal and limits you to having to recompile your library with a new InternalsVisibleTo-attribute if you want to add a new test assembly.

I appreciate your answer, though I think you may have slightly misunderstood me. I am trying to follow SOLID principles (hence 'split up into a lot of small components') - it's just that I don't think most of the classes in my project need to be public. The example in question was slightly exaggerated - there's more than just the facade that I want to expose, but I don't want to expose more than necessary. InternalsVisibleTo seems to be what I'm looking for, though. I'll wait to see if there will be any more answers, and mark yours as accepted if none is better. Thanks!
– user622505Mar 3 '16 at 10:27

I share the concerns about this answer. Having a few exposed methods with a lot of complexity beneath doesn't necessarily mean that the code requires extensive re-factoring.
– Robbie DeeMar 3 '16 at 10:48

The question did state that "...logic performed within what is called from that single method is very complex and testing directly on the ComplexProjectFacade gives not nearly enough certainty that all works correctly. " To me, that means you are still having classes that are too broad and should be focused. Aside from that the question itself to me basically boils down to how to expose a library that is composed of a large number of small classes with only one single class making sense to the consumers as an entry point. Hence the second part of my answer.
– JDTMar 3 '16 at 13:15

@JDT: "logic performed within what is called from that single method". To me this implies that the method calls other stuff, which does the complex logic, with no real indication that it does or does not follow SOLID nor is to broad - only that it is internal. As I said, I believe in this case InternalsVisibleTo is what I'm looking for, so the internal components will be testable, but this doesn't mean that any of them is too broad. The rest I agree with.
– user622505Mar 3 '16 at 15:01