Wednesday, April 2, 2008

stubbing out java.util.Random with EasyMock and Spring

Assuming you are familiar with Spring and EasyMock, here is a little tutorial on how to stub out randomness for your JUnit tests.

First I probably need to give a little context on how I have my test framework set up with Spring. I have two spring config files, one of them is just for testing and injects stubs/mock objects where appropriate. I have a base test class that extends AbstractDependencyInjectionSpringContextTests. It has members (and setters) for all of my classes that I want to test, and all of the stubs that get injected.

I make all of my classes to test scope="prototype" and my stubs are all singletons, so that I can easy get access to the same stub that will be injected into my class under test. Before running a test, I set up the stubs appropriately, and get them ready to replay. After the test, I call EasyMock.reset() to reset them for the next test (since they are singleton).

Ok, on to the example. Let's say you have a method that uses java.util.Random:


public class MyRandomClass
{
  private void Random rand;

  public int doSomethingRandom()
  {
    return rand.nextInt();
  }

  public void setRand(Random rand)
  {
     this.rand = rand;
  }
}


In your real spring config file you would have something like this:

<bean id="myRandomClass" class="MyRandomClass">
   <property name="rand" ref="rand"/>
</bean>

<bean id="rand" scope="prototype" class="java.util.Random"></bean>


And in your test spring config you would set up your 'rand' bean like this:

<bean id="rand" class="org.easymock.classextension.EasyMock" factory-method="createMock">
   <constructor-arg value="java.util.Random"/>
</bean>


Notice that I am using the EasyMock Class Extension because Random does not have an interface (Sun should add one, in my opinion). The regular EasyMock library can only mock interfaces, and the class extension adds the ability to mock classes themselves.

Now when setting up your test case, you can specify what 'random' values you want, like this:


EasyMock.expect(rand.nextInt()).andReturn(0);
EasyMock.expect(rand.nextInt()).andReturn(1);
...


And then you know what to expect:

assertEquals("returned wrong result", 0, myRandomClass.doSomethingRandom());
assertEquals("returned wrong result", 1, myRandomClass.doSomethingRandom());


That's it! (at least for this simple toy example)

One thing I noticed is that when using the EasyMock Class Extension, you have to remember to use the org.easymock.classextension.EasyMock createMock(), reset(), replay(), verify() etc. methods on any actual classes that you stub/mock. But I was able to use org.easymock.EasyMock.expect(), etc. methods when setting up the stubs.

2 comments:

  1. hi,
    nice blog, can you plz link the source code of blog ?

    thanks

    Kashif

    ReplyDelete
  2. import java.util.Random;

    import org.easymock.EasyMock;
    import org.junit.Assert;
    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;

    public class EasyMockLearningTest extends AbstractSACoreEjbSpringTestCase {

    @Autowired
    MyRandomClass myRandomClass;

    @Autowired
    Random rand;

    @Test
    public void testEasyMockBean() throws Exception {
    EasyMock.expect(rand.nextInt()).andReturn(0);
    EasyMock.expect(rand.nextInt()).andReturn(1);
    EasyMock.replay(rand);

    Assert.assertEquals("returned wrong result", 0, myRandomClass.doSomethingRandom());
    Assert.assertEquals("returned wrong result", 1, myRandomClass.doSomethingRandom());
    }

    }

    ReplyDelete