2013-03-29

7. Test behavior, not methods

I recently revieved some code from one of my colleagues. A birds-eye view of his class looked like the following. There was nothing wrong with it - it looked perfectly fine to me.

// Foo.java
class Foo {
  public void Foo(Bar bar) {...}
  public void methodOne(String str){...}
  public String methodTwo(){...}
  public Integer methodFour(Boolean b){...}
}

However, I read the test for this class first, FooTest. Even though it looked like it tested what it should test, I had some immediate comments after just a quick glance in the file.

class FooTest {
  @Test public void testMethodOne(){
    ...code...
    assert...
    ..reusing and modifying state..
    assert...
    ...more code...
    assert..
  }
  @Test public void testMethodTwo(){..more of the same..}
  @Test public void testMethodThree(){..more of the same..}

As many others, he had written one test for every method in the system under test (SUT). The methods was long. State bled through from the start of the test-method to the end of it. Variables were reused and their state modified as the test went along. I usually prefer to test behaviors, not methods, and I felt being on safe grounds when I suggested for him to rearrange his test so that he had one test per behavior.

He thought what I said was a ridiculous idea. As an example he pointed out one of my own tests that follows this pattern and looked something like the following.

class FooTest {
  @Test public void methodOne_leaves_foo_with_clothes_on(){...}
  @Test public void methodTwo_calls_mom(){...}
  @Test public void methodTwo_writes_result_in_book(){...}
  @Test public void methodOne_after_methodTwo_lights_the_firecrackers(){...}
  ...more tests...

"I hear what you say, but I think it is ridiculous. What do you need all those damned tests for? You have twice the amount of code in your tests than what you have in the classes you write. Plus. You are violating our coding standard with those underscores! Think about maintainability, man! And after all, this is just the test-code. It doesn't need that much focus." Awh the pain! I managed to resist the temptation to slap the offending party in the face with a fresh cod I usually carry around for that exact purpose. Instead I resorted to a reasoned debate and conversation.

In some respects he's right. It becomes more code. Much more code. But I've come to realize that organizing tests by methods does not tell me anything about the behavior of the SUT. In order to understand the behavior of Foo from above, I need to read all the code in the tests. I probably need to read all code in Foo as well. And from that I might be able to deduce what it does or what it's supposed to do. But even if I get thus far, it's still very difficult for me to understand what aspects of that behavior actually have a test and which one have not.

One test per behavior, allows a quick glance of the behavior of the class or subsystem just by collapsing the body of the test-methods. The naming of the tests can read as a story. IMO the layout and cleanlyness of the test-code is more important than the production code it self. If we need more words to communicate, write more words - communicating the intents clearly is more valuable than writing less code.

As Einstein said, paraphrased, make it as simple as possible, but no simpler.


No comments:

Post a Comment