Saturday, October 29, 2011

I Spy with My Little Mockito

Recently I was stuck trying to get some code under test. There was some complex code, with random generating ids, side effects, etc. Generally all the things that make testing hard.

I'm using mockito and I am not familiar with all its features, but I did know that it was supposed to give me the ability to partially mock. So away I went.

Remember this is a very contrived example.

package controller;

public class ComplexClass {

    String someComplexCode() {
        long num = Math.round(Math.random());
        if (num % 2 == 0) {
            return "Even";
        } else {
            return "Odd";
        }
    }

    public String callsComplexCode(){
        String output=someComplexCode();
        return output;
    }
}

This is the code I want to add

package controller;

public class ComplexClass {

    String someComplexCode() {
        long num = Math.round(Math.random());
        if (num % 2 == 0) {
            return "Even";
        } else {
            return "Odd";
        }
    }

    public String callsComplexCode(){
        String output=someComplexCode();
        if("Odd".equals(output)){
            output+=" man out";
        }else{
            output+=" More";
        }
        return output;
    }
}

And here are my tests that I want to run

package controller;

import static org.junit.Assert.assertNotNull;

import org.junit.Test;


public class ComplexClassTest {


    @Test
    public void testCallsComplexCode(){
        ComplexClass complexClass=new ComplexClass();
        String output=complexClass.callsComplexCode();
        assertNotNull(output);
    }

    @Test
    public void testCallsNewComplexCodeEven(){
        ComplexClass complexClass=new ComplexClass();
        String output=complexClass.callsComplexCode();
        assertEquals("Even More",output);
    }

    @Test
    public void testCallsNewComplexCodeOdd(){
        ComplexClass complexClass=new ComplexClass();
        String output=complexClass.callsComplexCode();
        assertEquals("Odd man out",output);
    }

}

As you can see the tests will fail since the output is randomly generated. This is actually a big problem with legacy code. I don't really care right now abut the method someComplexCode and I don't want to refactor it without some tests in place or taking a long time to try to understand it. I just want to add code to callsComplexCode.

What I need is a way of mocking the class that I'm testing. Partial mocks to the rescue.

Partial mocks give us the ability to mock sections of the code and use the actually code for other sections. This allows us to get some of the class under test. In an ideal world we should be able to test everything from public interface, but I have to deal with legacy code and this is a very powerful tool to test my changes.

And here is the new test



package controller;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

import org.junit.Test;


public class ComplexClassTest {


    @Test
    public void testCallsComplexCode(){
        ComplexClass complexClass=new ComplexClass();
        String output=complexClass.callsComplexCode();
        assertNotNull(output);
    }

    @Test
    public void testCallsNewComplexCodeEven(){
        ComplexClass complexClass=new ComplexClass();
        complexClass=spy(complexClass);
        doReturn("Even").when(complexClass).someComplexCode();
        String output=complexClass.callsComplexCode();
        assertEquals("Even More",output);
    }

    @Test
    public void testCallsNewComplexCodeOdd(){
        ComplexClass complexClass=new ComplexClass();
        complexClass=spy(complexClass);
        doReturn("Odd").when(complexClass).someComplexCode();
        String output=complexClass.callsComplexCode();
        assertEquals("Odd man out",output);
    }

}


Now I have the complex method under control and I can test my code.

Ok, sounds great but what are the drawbacks? Well all the methods to be mocked have to be visible to the test and non final. I wrote before about testing private method and I still tend to think its a bad practice, but I also think as a last resort its OK to use some of these techniques.

There is also a danger here is that we bypass code that really needs to be refactored. One of the reason this code is hard to test is its designed badly.

In the book Working Effectively with Legacy Code, Michael Feathers talks about using sprouts to bypass functionality we don't have the time to understand and growing new clean code in its place or around it. Also if some changes the someComplexCode our test will still pass since we are mocking the behavior. However,  to add some functionality and get that new code under test partial mocks could be a valuable tool.