What should a Unit Test test?

October 2, 2016

First off, I’m an Android developer and this article will be in the Contextcontext 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.

“…a single class”

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.

“…the public interface”

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”

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.

An example

public class MyClass {
  private boolean isDataLoading;

  public void loadData() {
    if(!isDataLoading) {
      isDataLoading = true;
      ourApi.getData().onFinish({ isDataLoading = false; });
    }
  }
}

Say we want to test that when data is loading, ourApi.getData is not called a second time.

Solution One

One way to do that is by changing isDataLoading to a non-private modifier and then checking:

myClass.loadData();
Assert.isTrue(myClass.isDataLoading);

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.

Solution Two

Instead, try this:

// CallbackThatNeverCompletes prevents onFinish from being called
when(ourApi.getData()).thenReturn(new CallbackThatNeverCompletes());
myClass.loadData();
myClass.loadData(); // loadData is called twice
verify(ourApi, once()).getData(); // Check that the api is called only once

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.


Follow @davidwhitman    Star    Issue

Android Dev Testing