Saturday, January 28, 2012

Mockito is just awesomeness

Last week I ventured into making some enhancements into a project that is completely unknown to me. I see unit tests as my writ of passage before I can checkin any code into a project new to me --not to say I should ever checkin code without tests, but I think you all know what I mean.

Writing tests for an unknown code is either difficult or tiresome; difficult, if I decide to understand large chucks of the project before writing a lick of code; tiresome, if I chose to only understand the module that I am changing and just mock the rest of classes when unit testing.  In reality, It should be a bit of both.

In the past I made lots and lots of mock objects by making classes that implement a certain interface or try to instantiate a class with lots of dependency, when really, I only care about one or two methods in the class. It's a tiresome, verbose, and just plain an ugly thing to do for writing a unit test.

Fortunately, I came across Mockito with allows you to mock and stub classes effortlessly.

Take the StatsManager class that has a genderCount() method.

public class StatsManager { 


    private JDBCTemplate template;
  

    public long genderCount(Gender gender) {
       //perform some business logic 
       
       ...
    } 
    
    public void setJDBCTemplate(JDBCTemplate template) {
      this.template = template;
    }
    
... 


}


Say I want to make a change to the genderCount() method and I need an unit test to validate my change.

Before Mockito, I would need to instantiate an instance of JDBCTemplate.  It happens that JDBCTemplate needs an javax.sql.DataSource instance to work. I would then try to make a mock class of this interface, but then it turns out that javax.sql.DataSource has a dependency javax.sql.Connection so I have to mock that class and world goes round and round... but I never finishing mocking!

What is it that I really need in order to test this code? It's the logic in genderCount() that I want to test, and don't want to concern myself with all these other setup classes/interfaces. Infact, I know that queryAsList(String sql) is the only method called on the JDBCTemplate class in StatsManager.genderCount() . All I want is a certain List of results when genderCount() is called.

This is exactly what Mockito give me! Here is an example

public class StatsManagerTest {

@Test
public void testGenderCount() {
    

    StatsManager manager = new StatsManager();


    JDBCTemplate template = mock(JDBCTemplate.class)


    List results = new ArrayList();
    //initialize list.
    ...

    when(template.queryAsList(anyString())).thenReturns(results);

   
    long maleCount = template.genderCount(Gender.MALE);
    //Assert a bunch of stuff
    ...


    long femaleCount = template.genderCount(Gender.FEMALE);
    //Assert a bunch of stuff
    ...

}


}



The key thing to notice here is the  when() and thenReturns() pair. What this is doing is that when queryAsList() is called regardless of what string is passed as a parameter, return this fixed list of results.
That's it! My unit test is concise/readable, free from a bunch of setup code.   I can always setup a different list of results to test against in my unit test.

It makes writing unit tests fun versus a necessary evil.

3 comments:

Lukas Eder said...

Instead of mocking the complete JDBCTemplate type, you could also mock JDBC at a lower level using jOOQ's mocking capabilities as described in this blog post:

http://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/

With that technique, you'll only have to implement one MockDataProvider method, instead of all of the Methods from JDBCTemplate.

Anonymous said...

Sorry Lukas, but unless i am wrong in some way, it is not possible to mock jdbc at the lowest level using jOOQ.
Actually the data provider is not the only thing you have to implement. You also have to create Records. And that becomes a nightmare when testing code with no database at all, and if you don't want to generate code.

There is no public accessible Record implementation one can use or extend easily, without generating code.

Note : I was trying this in an application without jOOQ. I just wanted to test my dao by mocking jdbc.

Lukas Eder said...

Sorry for the late reply. Perhaps it's difficult, but I don't see how it would be easier with JDBC directly...

Note, we're constantly adding new API to generate Result or Record objects...