First off, I’m an Android developer and this article will be in the
context of Android. However, the principles are applicable to any tests you may write.
What should a Unit Test test?
A unit test should test the behavior of the public interface of a single class.
Let’s break that down.
That should be nothing new. A unit test should test a single class and its dependencies should be mocked. This gives us total control over the test and ensures that it will only fail due to changes within the class.
This is mostly a fancy way of saying, “the public methods”. If you created an
interface for your class, that’s what you would test. A unit test shouldn’t have any special access to your class - otherwise, you risk testing how your class works, and not only what it does.
The behavior is what a method does; what it returns and anything that it causes to happen to external classes (events thrown, method calls, etc). Behavior is not how a method does something; that is, any methods it calls within the same class, any private fields it modifies, etc. If we test how a method does something, then another developer may change the how without changing the behavior and the test will break even though the code still works.
Say we want to test that when data is loading,
ourApi.getData is not called a second time.
One way to do that is by changing
isDataLoading to a non-private modifier and then checking:
BUT now we’ve done a few bad things.
Firstly, we’ve modified our class in order to ease testing - not a good sign (note: I’m not talking about refactoring your class to allow unit tests to be written; that’s a different thing entirely). We’ve made the
isDataLoading variable externally accessible. Subclasses or other classes in the same package might be able to change it now, and that’s not desirable.
Secondly, the test isn’t doing what we set out to do. The test is called “when data is loading,
ourApi.getData is not called a second time”, but it’s actually testing that “when data is loading,
isDataLoading == true”.
Finally, our test is now dependent on the inner workings of
MyClass. Another programmer could come into the class and refactor how it works without changing what it does; that is, none of the classes using
MyClass would need to change; and our test would break. A test shouldn’t break if somebody refactors the class (not changing functionality) but doesn’t need to change any classes that use it.
Instead, try this:
We no longer need to modify
MyClass and we no longer depend on an implementation detail of
MyClass; somebody else could refactor the class without changing its behavior and our test wouldn’t break.
Hopefully this short overview has been of use. The example was contrived, but it’s the theory that’s important. There’s a lot to talk about in unit testing, so if you’d like to see more then let me know and I may be persuaded to spit out some more posts.