Writing Code that is Unit Test Friendly


Unit test code is probably just as important as the production code. Not only does it prove that your production code works as expected, it also serves as the regression suite to ensure the feature is not broken in the future.  I would argue that there should be more test code than production code.  With that in mind, it pays to write your code in a way that’s easy to write unit tests for.

However, if you are not following the TDD approach and only start writing unit tests after you have finished coding for the feature you’re implementing, you may end up having a hard time to test your code because of the way it’s written. Here are some tips to avoid the heart attack when you start writing unit tests and find that impossible. The tips are all derived from problems I’ve seen in  our real production code.

1. Make sure the result of the method invocation is visible either through the return value or the input arguments.

Here’s an example taken out of our production code that goes against the rule above:

public void doSomething(ValueObjectClass vo)

    // fetch the domainObject using vo and do something

The method doesn’t return any value. All input arguments are acting as value objects, meaning they are used only to transfer values. That means from outside of the method, nothing has changed. The change is materialized in the domainObejctDAO.save() call. In unit test code, domainObjectDAO is often a mocked object (which is the right thing to do since we want to decouple our code from external dependencies.). This makes is very difficult to verify that doSomething() is doing the right thing because we have nothing to verify against. We can’t check return values because there’s none. We can’t check the input argument value changes because they don’t change. And we can’t query database for value changes even if I want to because the mock DAO doesn’t save it into the database at all.

A better approach is to make doSomething() itself returns the modified object (or change the value of the input arguments, whichever is appropriate). Have another methodcallDoSomething() that calls doSomething() and domainObjectDAO.save(). So the unit test for doSomething() can verify the values are correct. And the unit test forcallDoSomething() can verify that both doSomething() anddomainObjectDAO.save() are invoked, assuming both doSomething() anddomainObjectDAO.save() are tested separately by other unit tests.

An even better approach, if you follow the domain driven design principle (DDD), is to move doSomething into your domain model (instead of the domain models being anemic with nothing but getters and setters). So you test your domain model’s doSomething() method without worrying about how it’s persisted. And write a service layer code which will call your domainObject.doSomething() as well as DAO.save(). To test the service layer code, you only need to verify that both doSomething() and save() are invoked. You can read more about anemic domain model here. Unfortunately, almost all “domain” objects I see in our production code are anemic.

If you are unlucky that you have to deal with testing behavior that’s not visible externally as in the example I gave above, fortunately Mockito has a feature called Captor that allows you to capture the arguments that are passed to a mocked object. You can use Captor to verify the values of the domainObject. Here’s how you test the example above:

//Alternatively, there’s also a @Captor annotation that works at class level (not within the method)
ArgumentCaptor<DomainObjectClass> argument = ArgumentCaptor.forClass(DomainObjectClass.class);

DomainObjectClass domainObject = argument.getValue(); 

//Do some verification of values in domainObject
Assert.assertEquals(domainObject.getSomething(), “It works!”);

2. Don’t use static method if you can avoid it

This is not a hard rule, more of a limitation of the Mockito testing framework as it doesn’t support mocking of static methods. Checkout their FAQ for Mockito’s limitations. There are some test frameworks that support mocking of static methods. But based on my personal experience, Mockito is the most straightforward to use so far. So I would stick to it for as much as you can.

So why should I not use static method and what do I do if I’m testing legacy code that has static methods? Here’s in Mockito’s own words:

Can I mock static methods?
No. Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change. If you deal with scary legacy code you can use JMockit or Powermock to mock static methods.

Basically, if you’re writing new code, write it as non-static method and use dependency inject to inject singleton instance. And to test legacy static methods, use some other test framework that supports it. In my experience, I found JMockit and Mockito can co-exist in the same test code without a problem. Here’s the code to test static method using JMockit to stub the return value of a static method call:

new NonStrictExpectations() {
SomeClass mockSomeClass;
mockSomeClass.aStaticMethod(__arguments__); result = __stubbed_result__;

3. Try to minimize dependencies

If you write unit tests first as in TDD, or as soon as possible (when you have some code that can compile and run, with placeholders for things not implemented yet), it will help you to identify clutter-ness of your code and make your code more modular. Modular code is easier to test as there are less dependencies to satisfy. It’s quite annoying when you have to mock or stub 10 interfaces/classes for the test setup because your implementation needs them all. It’s also code smell that your implementation is difficult for other people to use since they’ll face the same problem. If you are facing this situation, think hard about how you can make your code more modular so that some dependencies are moved or removed. If your method needs 10 external dependencies, chances are your method is growing too big and you should divide it into smaller logical pieces, each with less dependencies. That makes your code easier to read and understand as well.

That’s all for my first ever technology blog. Phew~~ I noticed that each section is shorter than the one before it, a sign of me getting more and more mentally exhausted.  Blogging is hard!

P.S. Stubhub HIRING! We need talent. Come and join us!

©2012 StubHub, Inc. All rights reserved.