Coding | Examples | On Unit Testing

Should you test non-public methods?

The reason why you should is fairly easy to answer here: if it can break, you might want to test it. Private methods can break, and so they should be tested. Of course, they're indirectly tested when you test the public methods, but a failing test on a private method gives you a closer indication of where the source of the problem is; incorrect functionality in the private implementation of a class can manifest itself in myraid and strange ways when you're at the level of the public interface, so tests happening at the private methods level may give a more true representation of what really went wrong.

The reason why not, however, makes the very compelling point that by adding testing code that relies on the implementation details of a class, you radically increase the amount of work you have to do when refactoring. If you keep your tests at the level of the public interface of a class, you can indirectly test that the private details of the class are working correctly, but you don't give yourself any more work when refactoring, because the private methods are truly private and can be changed without needing to change any test cases.

I think that, ideally, you should keep testability in mind when designing the public interface itself, and make enough information available through the public interface so that you don't have to test private methods. If the implementation is so complicated that you really need to directly test it, then it should probably be refactored out into another class with its own public interface that you can test, which is then used in turn by the class you started with for its implementation.

Should you test simple one-line functions?

The argument against testing one-line functions is that they can easily be scanned for errors, and it's more trouble than it's worth to test them. This may be true for some really simple functions (for example, pass-through wrapper accessor methods for private member variables), but in general, just because you can scan easily for errors in a single line doesn't equate to being able to scan automatically for errors. Part of the point of unit testing is the ability to run regression tests automatically on an entire codebase. If you leave out tests on one line functions, you may break something in refactoring that you don't catch in tests.

Ultimately, this is a judgement call. If you really think it's trivial and not worth testing, you may be right; but if you later come back and find that the one line function wasn't as trivial as you thought, you should adjust your perception of "trivial" accordingly (this is a position we have probably all been in as software engineers - realizing that the thing you once thought was trivial is actually a little bit more complicated or important than you thought.)

Should tests hit non-public attributes and methods, to query the internal state of a component?

This is less straightforward than the questions above. Using non-public attributes to query the internal state of a component can be very useful in testing - for example, you might want to check an invariant procedure like tree_ok after a tree operation, but you might not want that method to be part of the public interface.

The opposite point of view, however, is that if these are things that can't be queried in the real public interface, then it may be unrealistic to make use of them in unit tests. For example, you could (inadvertantly or otherwise) write a unit test that first checks the private internal state before calling a method, which you couldn't do in real code. This may make the unit test less useful than it could be otherwise.

Ultimately, if you do have invariants or other private internal state within in object that should be tested for, a better way to solve this problem is by declaring assertions within the real methods. That way, if the unit test could trigger the invalid state, the method will throw an exception, and the test will still fail. Likewise, if there are internal state variables that are required for using the class in a realistic testing situation, it's likely that those state variables should be exposed (though perhaps not directly) in the public interface itself.