7

I'm aware of (and agree with) the usual arguments for placing unit tests in a separate assembly. However, of late I've been experiencing some situations where I really want to be testing private methods. The behind-the-scenes logic in question is complex enough that testing the public and internal interfaces doesn't quite get the job done. The testing against the class's public interface feels overwrought, and I see several spots where a few tests against privates would get the job done more simply and effectively.

In the past I've tackled these kinds of situations by making the stuff I need to test protected, and creating a subclass that I can use to get at it in the test framework. But that doesn't work so well on classes that should be sealed. Not to mention bloating the test framework with all that scaffolding.

So I'm thinking of doing this instead: Place some tests in the class, where they can get at the private members. But keep them out of the production code using '#if DEBUG`.

Does this seem like a good idea?

Sean U
  • 6,730
  • 1
  • 24
  • 43
  • 5
    If the internal logic is so complex, maybe the private methods need to refactored to separate classes. – Joe Jun 14 '12 at 15:32
  • Further reading: [How do you unit test private methods?](http://stackoverflow.com/questions/250692/how-do-you-unit-test-private-methods) Ignore the marked answer. – Adam Houldsworth Jun 14 '12 at 15:34
  • @Joe I agree that's a very important design principle. But every 'maybe' has its 'maybe not'. This is one of the 'not' situations. – Sean U Jun 14 '12 at 15:40
  • @SeanU If this is just a one-off, reflection is a *good enough* "lesser evil". – Adam Houldsworth Jun 14 '12 at 15:43
  • @Sean, OK, but you're asking a general question, and the general answer is to go for a more testable design. Maybe by refactoring the private stuff, maybe by unsealing and using protected. But what you're proposing will work of course, and all projects of any size end up with ugly hacks somewhere, so I wouldn't worry about it ... – Joe Jun 14 '12 at 15:45

3 Answers3

9

Before anybody asks...

The solution to OP's problem is to properly incorporate IoC with DI and eliminate the need of testing private method altogether (as Joel Martinez noted). As it's been mentioned multiple times, unit testing private members is not the way to go.

However, sometimes you just can't change the code (legacy systems, risk of breaking changes - you name it) nor you can use tools that allow private members testing (like Typemock, which is paid product). For such cases, you can either not test at all, or cut corners. Which I believe is situation OP's facing.


Leaving private methods testing discussion aside...

Remember you can use reflection to access and invoke private members.

In my opinion, placing conditional debugs in the class itself is rather bad idea - it adds noise (as in, something unrelated) to the class code. Sure, it will be gone in release, but you (and possibly other programmers) will have to deal with it on the daily basics.

I realize your idea might sound good on paper - simple test wrapped with conditional debug. But in reality, tests quickly turn out to use extra variables (those will also have to be placed in the class code), some utility (extra references, custom types), testing frameworks (even more references) and what not. This all will have to be somehow connected to the class code. Put that all together, and you quickly end up with an unmaintanable monster.

Are you sure you want to deal with that? Especially considering that throwing together simple reflection-based utility is probably not that hard.

Community
  • 1
  • 1
k.m
  • 30,794
  • 10
  • 62
  • 86
  • +1 I think reflection used in unit tests in special or rare cases is an acceptable "lesser evil". Using it in a blanket manner to test all private methods is probably going to lead to trouble down the line. – Adam Houldsworth Jun 14 '12 at 15:44
  • That said, I would personally attempt to refactor for testability. – Adam Houldsworth Jun 14 '12 at 15:52
  • Thanks! Using reflection is exactly what I was forgetting. – Sean U Jun 14 '12 at 15:55
  • I really think a much better option is to refactor to extract the private method into another class and inject it through the constructor. Testing private methods through reflection is a smell IMHO. – Sergio Romero Jun 14 '12 at 19:40
  • @SergioRomero, Adam: I am perfectly aware that using reflection to test private members is not the way to go. I explicitly stated that I am leaving this discussion aside, as it's been already mentioned (by Adam and Joel) and OP also noted he *knows* he shouldn't do that. *The solution* to OP's problem is **in most cases** IoC/DI. Unfortunately, there are extreme situations where you just have to get your hands dirty. And if you do, do it right. – k.m Jun 14 '12 at 20:15
  • I don't disagree, that's why I upvoted. I was just emphasizing that it shouldn't be used as the first option where possible. In this situation I'd be happy to do the same. – Adam Houldsworth Jun 14 '12 at 21:10
  • 2
    I want to just chime in to agree with the above, for the sake of anyone else looking at this question. This approach isn't really a clever trick so much as a last-resort coping mechanism. Even so, velociraptors are clawing at my door as I type this. – Sean U Jun 14 '12 at 22:52
7

Everything you're referring to can be solved with just two concepts: Single Responsibility Principle, and Dependency Injection. It definitely sounds like you need to simplify your classes. Mind you, that doesn't mean the class must offer less value, it just means that the internals need to be simpler and some functionality may have to be delegated to others.

Joel Martinez
  • 46,929
  • 26
  • 130
  • 185
  • 2
    +1 because I completely agree that dependency injection is the best answer to this problem. – Katie Kilian Jun 14 '12 at 15:45
  • I wish it were so. But the class does have a single, non-divisible responsibility, and it has no dependencies to inject. This is an admittedly special case - which is why I'm exploring for a special solution. – Sean U Jun 14 '12 at 15:54
  • 2
    @SeanU In my answer I discuss the idea that the dependency is on the private method itself. I would love to see the code and know the reasoning why you cannot satisfy this implementation through testing public members. – Adam Houldsworth Jun 14 '12 at 15:55
4

If you need to test this method independently of the public API of the class, then it sounds like a candidate for being removed from the class itself.

You could say the class is dependent on the private method (as is arguably evident by the need to test it separately from the class public API).

If this dependency cannot be satisfied through testing the public API of the type alone then have the class instead delegate this dependency to another type. You can either instantiate this type internally or have this type injected / resolved.

This new type can then have its own unit tests, as it's public API will be expressing what was previously a private method.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187