Thursday, December 29, 2011

EasyMock Fundamentals


Introduction

EasyMock is a mock library that can be used as a supplemental tool for a testing framework, such as TestNG, to improve unit testing implementations. EasyMock provides the means (through the use of mock objects) to specify the response that will be returned when the code being tested makes calls to external methods. This will allow the result of the unit test to be isolated from variables outside of the scope of the unit test code.

Refer to the official EasyMock documentation for more detailed explanation of the tool (latest as of this writing EasyMock3.0.Docs).

Note: For the sake of simplicity, not all the details of the code examples have been included.

What is a mock?

Short concise description:
Mocking and Mock Objects is not specific to Java. Mock objects is a unit testing technique in which a code chunk is replaced by dummy implementations that emulate real code. This helps one to write unit tests targeting the functionality provided by the class under test. - reference
For more information, this article offers very good introduction to the concept of mocking:
Mock Objects.

Stubs vs. Mocks

An alternative to using a mock object is to create a stub object; a lightweight implementation of the interface for the external methods, that just return the expected response. These two approaches are best illustrated with an example.

Let us say you want to write a unit test for an object Foo that does something cool, doSomethingCool(), and uses another object, Emailer, to send emails while doing something cool as follows:
package com.partnet.foo;

public class Foo {
  private Emailer emailer;

  public Foo(Emailer emailer) {
    this.emailer = emailer;
  }

  public void doSomethingCool() {
    // something worthy of being unit tested

    emailer.sendEmail(...);
  }
}

package com.partnet.foo;

public interface Emailer {
  boolean sendEmail(...) throw EmailerException;
}
If you're trying to test the doSomethingCool() method, you don't want to, shouldn't have to, and thankfully don't need to worry about what emailer.sendEmail() does (assuming the implementation of the call has no impact on the code you're testing of course).

The assumption here is that the real implementation of Emailer, say it's called FancyEmailerImpl, is a non-simplistic object that may require some configuration or won't work in a non-real environment. In this case you do not want to have to create a real FancyEmailerImpl and use it in your testing because again, what it does has no bearing on the code you're actually trying to test.

Stub Objects

Using the stub object approach, you could create a stubbed-out/dummy version of Emailer like the following:
// stub object
package com.partnet.foo;

public StubEmailer
  implements Emailer
{
  public boolean sendEmail(...) throws EmailerException {
    return true;
  }
}
Simple, easy, done... right? Yep, you can now just setup your unit test as follows and test away:
// stub object use
package com.partnet.foo;

public TestFoo {
  @Test
  public void test() {
    Emailer stubEmailer = new StubEmailer();
    Foo myFoo = new Foo(stubEmailer);

    myFoo.doSomethingCool();
  }
}
The downside to this approach is that you end up building and maintaining a bunch of stub code. Additionally, stub code can be difficult to write as you get into stubbing out more complex objects that you want to test around. For example, if the code you're testing sets an ID on your Emailer and later retrieves it via setId()/getId() methods, this behavior would need to be stubbed out correctly.

Mock Objects

The alternative to writing stubs is writing mocks, or more correctly, using a mock object library to create mock objects for you. EasyMock is such a library. The following demonstrates how to use EasyMock to mock out the Emailer from above.
// mock object example
package com.partnet.foo;

public TestFoo {
  @Test
  public void test() {
    Emailer mockEmailer = EasyMock.createMock(Emailer.class);
    Foo myFoo = new Foo(mockEmailer);

    myFoo.doSomethingCool();
  }
}
Simple, easy, no stub code to maintain. Even cooler is how you solve the problem mentioned above regarding setId()/getId(). This is where the concept of Expectations comes in (in terms of EasyMock, these are called IExpectationSetters). An Expectation is basically you telling a mock object how to behave and what you expect to happen when you run the unit test code: "When this happens, do this", "When your getId() method is called, return this value", "I expect that your setId() method will be called". After your unit test code has run, you can then ask EasyMock to verify that everything you expected to happen actually occurred so that you can be assured your code does what you think it does.

Downloads

The latest EasyMock files can be downloaded from a link on the EasyMock Download page.

Integration

Add these to the classpath:
  • easymock.jar
  • cglib (2.2) - to perform class mocking
  • Objenesis (1.2) - to perform class mocking
EasyMock only works with Java 1.5.0 and above.

Writing Tests

Basic steps for EasyMock use

  1. import EasyMock
    • import static org.easymock.EasyMock.*;  // non-static imports may also be used
  2. create mock object
    • private ClassOrInterface mock;
    • mock = createMock(ClassOrInterface.class);  // throws AssertionErrors for all unexpected method calls
    • mock = createNiceMock(ClassOrInterface.class);  // allows all method calls and returns appropriate empty values (0, null or false)
  3. specify expectations for mock (in other words, "I'm going to tell you what you should expect to do")
    • mock.doThis();
    • mock.doThat();
    • specify that the last method could be called x times
      • expectLastCall().once();  // default one call
      • expectLastCall().times(3);  // exact calls
      • expectLastCall().times(2, 5);  // min, max calls
      • expectLastCall().atLeastOnce();  // one or more times
      • expectLastCall().anyTimes();  // 0 or more times
    • specify method response or return value
      • expect(mock.doThis()).andReturn(returnValue, i.e. "true");
  4. make mock available for use (in other words, "I'm done telling you what you should expect to do, now get ready to do it.")
    • replay(mock)
  5. run unit test that will call doThis() and doThat()
    • verify proper response
      • assertTrue(doThis());
  6. verify that expected behavior was called (did everything get called the expected number of times?)
    • verify(mock)

Tips

Prefer Nice Mocks

There are basically 3 different ways to have EasyMock create a mock object for you.
  1. createMock(Foo.class)
  2. createNiceMock(Foo.class)
  3. createStrictMock(Foo.class) 
Each way gives you a mock object with slightly different behaviors/restrictions. Consult the EasyMock API for specifics, but unless you have valid reason to not, the preferred method is createNiceMock(). Nice mocks are, as their name suggests, 'nice'. 'Forgiving' is probably a more appropriate term in that, as the API states, nice mocks will return 0, false, or null for unexpected invocations. Meaning:
  • for methods that return int, double, long, char, float, byte, short (non\-boolean primitives) - the returned value is 0 
  • for methods that return boolean (not sure about Boolean) - the returned value is false 
  • for methods that return Object (or anything that extends Object) - the returned value is null 
The benefits are not obvious without further explanation. Try to think of it in these terms. Nice mocks allow you to configure them the way you want for the behavior you care about without having to configure them for the behavior you don't care about and are not relying on for your test. For example, let's assume you want a mock for an object that has a getId() method. Chance are you don't care what getId() does and if the code you're testing doesn't care what it does, but happens to call it in a log statement or something that is irrelevant to your test, then a nice mock will allow you to not have to setup an expectation for that method (see Example 1).
package com.partnet.foo;

public class Foo {
  private Emailer emailer;

  public void doSomethingCool() {
    // I don't care about this and don't want to setup an expectation for it
    log.debug("emailerId:" + emailer.getId());

    // something worthy of being unit tested
    emailer.sendEmail(...);
  }
}
Additionally, if another developer were to remove that log statement, the unit test wouldn't have to change because the expectation was never added and the tests wouldn't break just because a log statement was removed.

Prefer "Relaxed" Expectations

The term "relaxed" refers to the IExpectationSetters API as it relates to configuring the number of times an expectation is valid. Specifically, the methods once(), times(count), and times(min, max) should be avoided unless you really care and/or your test relies on them. The methods anyTimes() and atLeastOnce() should be used instead. These "execution count" methods set the number of times a given method is expected to be called. The reason the latter methods should be preferred is that, while setting up explicit method call counts is certainly valid, implementations change over time and often times those changes have no effects on the functionality of a given object. For example, who cares how many times emailer.getId() is called in the above example and does the fact that it gets called multiple times have any effect on the calling code? Clearly not. However, if an expectation like the following was added:
EasyMock.expect(mockEmailer.getId()).andReturn(1).once();
and later the log statement was removed or an additional log statement was added that called getId() again, the unit test would fail.

However, this practice is not an across-the-board standard as it makes sense to setup an expectation like the following:
EasyMock.expect(mockEmailer.sendEmail(...)).andReturn(true).once();
because sending multiple emails is most likely not expected or okay.

NOTE: not specifying one of the "execution count" methods is the same as using the once() method (i.e. once() is assumed unless otherwise instructed).

Stub Returns

andReturn() vs. andStubReturn()

EasyMockSupport

Introduced in version 2.5.2, EasyMockSupport is your friend. It saves you the trouble of having to call reset(), replay(), and verify() on all your mock objects. As the javadoc shows, an example usage is as follows:
package com.partnet.foo;

public class TestFoo
  extends EasyMockSupport {  // this is key

  @Test
  public void test() {
    resetAll();  // reset all existing mocks

    // create mocks
    firstMock = createMock(A.class);
    secondMock = createMock(B.class);

    replayAll();  // put all mocks (new & existing) in replay mode

    // use mocks

    verifyAll();  // verify all mocks (new & existing)
  }
}

Mock Builders

See [some future post] for how cool this feature is.

Argument Matching

Argument matching is related to setting up expectations and is one of those subtleties of EasyMock that you might miss and certainly not understand until you really start writing tests.

The following shows setting up an expectation without any argument matchers:
EasyMock.expect(mockEmailer.sendEmail("foodaddy")).andReturn(true);
In this scenario, you're telling EasyMock to return true when sendEmail() is called with "foodaddy" as the parameter. Simple enough, but what if you wanted tell EasyMock to return true for all parameter values, or only certain parameter values, or only on Tuesdays, etc? This is where argument matchers come to play.

The following demonstrates an argument matcher that matches all String values:
EasyMock.expect(mockEmailer.sendEmail(EasyMock.isA(String.class))).andReturn(true);
What about all String values except "foodaddy"?
EasyMock.expect(mockEmailer.sendEmail(
    EasyMock.not(EasyMock.eq("foodaddy")))).andReturn(true);
EasyMock supports a wide selection of argument matchers that can be used to create any type of matcher. Here are some of the matcher methods:  anyInt(), anyObject(), contains(), eq(), geq(), gt(), isA(), leq(), lt(), same(), startsWith().

Argument matchers can be combined using the following methods:  and(), or(), not()

Consult the API for a complete listing, but as an example:
// true for "foodaddy" or "foo.*daddy"
EasyMock.expect(mockEmailer.sendEmail(
    EasyMock.or(EasyMock.eq("foodaddy"),
        EasyMock.and(EasyMock.startsWith("foo"),
                     EasyMock.endsWith("daddy")))
        )).andReturn(true);
EasyMock even has support for creating custom argument matchers. Consult the API for more info. For more info on argument matchers, see this blog post.

Argument Matching part 2

EasyMock has this annoying feature/limitation that prevents mixing non-argument matchers (raw values) with argument matchers when setting up expectations. I often encounter this when trying to setup expectations for a method that takes multiple parameters where some are simple primitives and others are complex types. This can be annoying because often times you want to be able to setup your expectations in this way. Given the following:
public interface Emailer {
  boolean sendEmail(String emailAddress, String message);
}

public class Foo {
  private Emailer emailer;

  public Foo(Emailer emailer) {
    this.emailer = emailer;
  }

  public void doSomethingCool(boolean useDefault, String emailAddress) {
    String message = null;
    if (useDefault) {
      message = "default message";
    }
    else {
      message = "something else";
    }
    emailer.sendEmail(emailAddress, message);
  }
}
So now you want to test this code so you start as follows:
public class TestFoo {
  @Test
  public void test_doSomethingCool()
  {
    final String emailAdrs = "foo@foodaddy.com";

    Emailer mockEmailer = EasyMock.createMock(Emailer.class);

    // return true when emails are sent to foo@foodaddy.com
    // regardless of the message being sent
    EasyMock.expect(mockEmailer.sendEmail(
        emailAdrs, EasyMock.isA(String.class))).andReturn(true);

    EasyMock.replay(mockEmailer);

    Foo myFoo = new Foo(mockEmailer);
    myFoo.doSomethingCool(true, emailAdrs);

    EasyMock.verify(mockEmailer);
  }
}
As the comment above states, you don't care what message is being sent to "foo@foodaddy.com". You want all emails sent to "foo@foodaddy.com" to return true. Seems valid enough, but when you run your test, you'll get an error message similar to this:
java.lang.IllegalStateException: 2 matchers expected, 1 recorded. This exception usually occurs when matchers are mixed with raw values when recording a method: You need to use no matcher at all or a matcher for every single param:

Huh? The key is "..._matchers are mixes with raw values_...". Unfortunately, you can't do what you were hoping to do. EasyMock requires that you use ALL raw values or ALL argument matchers in a given expectation. So the following shows how you accomplish this:
public class TestFoo {
  @Test
  public void test_doSomethingCool()
  {
    final String emailAdrs = "foo@foodaddy.com";

    Emailer mockEmailer = EasyMock.createMock(Emailer.class);

    EasyMock.expect(mockEmailer.sendEmail(
        EasyMock.eq(emailAdrs), EasyMock.isA(String.class))).andReturn(true);

    EasyMock.replay(mockEmailer);

    Foo myFoo = new Foo(mockEmailer);
    myFoo.doSomethingCool(true, emailAdrs);

    EasyMock.verify(mockEmailer);
  }
}
The eq() argument matcher is what's needed here and is especially handy at matching primitive values. The eq() argument matcher can certainly be used to match more complex types, but with primitives, you don't have to worry about the implementations of equals() and hashCode() as discussed next.

Captures

These are cool, but the documentation and examples I've found are not. I like examples so hopefully the following will demonstrate Capture's coolness. Given the following:
public class Recipient {
  private String name;
  private String email;

  public Recipient(String name, String email) {
    this.name = name;
    this.email = email;
  }

  // getters for above attrs
}

public interface Emailer {
  boolean sendEmail(Recipient r, String message);
}

public class Foo {
  private Emailer emailer;

  public Foo(Emailer emailer) {
    this.emailer = emailer;
  }

  public void doSomethingCool(boolean useDefault) {
    Recipient r = null;
    if (useDefault) {
      r = new Recipient("foodaddy", "foo@part.net");
    }
    else {
      r = new Recipient("noreply", "noone@part.net");
    }
    emailer.sendEmail(r, "some message");
  }
}
So the question is, how would you test the doSomethingCool() code and ensure that the if/else conditional evaluation is being done correctly? Granted, this is a simple example, but the question is still valid. There are 2 possibilities.
  • 1) Implement equals() and hashCode() on the Recipient object so that their implementation, specifically equals(), could be used in your unit test as follows.
public class TestFoo {
  @Test
  public void test_doSomethingCool()
  {
    {
      Recipient recOne = new Recipient("foodaddy", "foo@part.net");
      Emailer mockEmailer = EasyMock.createMock(Emailer.class);

      // this next line is the key - we're relying on the fact that
      // recOne.equals(recipient) == true - where recipient is the
      // one created in doSomethingCool()
      EasyMock.expect(mockEmailer.sendEmail(recOne, ...)).andReturn(true);

      EasyMock.replay(mockEmailer);

      Foo myFoo = new Foo(mockEmailer);
      myFoo.doSomethingCool(true);

      EasyMock.verify(mockEmailer);
    }

    {
      Recipient recTwo = new Recipient("noreply", "noone@part.net");
      Emailer mockEmailer = EasyMock.createMock(Emailer.class);

      // same comment as before
      EasyMock.expect(mockEmailer.sendEmail(recTwo...)).andReturn(true);

      EasyMock.replay(mockEmailer);

      Foo myFoo = new Foo(mockEmailer);
      myFoo.doSomethingCool(false);

      EasyMock.verify(mockEmailer);
    }
  }
}
As was noted in the code, the key is that we're relying on the implementation of Recipient.equals().

On the test code line:
mockEmailer.sendEmail(recOne, ...);
two things are occurring:
  1. You are setting up an expectation that the sendEmail() method will be called
  2. The Recipient that is passed to sendEmail() will be equal to the one provided. 
Even though the same Recipient object instance (recOne != recipient) is not used in the expectation, the two different instances are considered equal:
recOne.equals(recipient) == true
By not including an argument matcher explicitly, EasyMock is using object equality (equals()) to match. This is the same as using the eq() argument matcher.

While this approach is certainly valid, it's less than ideal because it requires that equals() be implemented. Furthermore, the implementation of equals() has to be known and understood how to be taken advantage of (i.e. you have to know that name and email are used in equals() rather than something like an id attribute). Additionally, you're not able to actually test/assert against the attribute values of the created Recipient. That's essentially what's being done by taking advantage of the implementation of equals(), but it's less explicit and obvious to the poor maintenance developer.
  • The second (and better) approach is to use an EasyMock Capture as follows to retrieve, err capture, the actual instance of the Recipient object created by the doSomethingCool() method.
public class TestFoo {
  @Test
  public void test_doSomethingCool()
  {
    {
      Emailer mockEmailer = EasyMock.createMock(Emailer.class);
      
      Capture rCap = new Capture();

      EasyMock.expect(mockEmailer.sendEmail(
          EasyMock.capture(rCap), ...)).andReturn(true);

      EasyMock.replay(mockEmailer);

      Foo myFoo = new Foo(mockEmailer);
      myFoo.doSomethingCool(true);

      EasyMock.verify(mockEmailer);
      
      // actual Recipient instance returned here
      Recipient recipient = rCap.getValue();
      
      Assert.assertEquals(recipient.getName(), "foodaddy");
      Assert.assertEquals(recipient.getEmail(), "foo@part.net");
    }

    {
      Emailer mockEmailer = EasyMock.createMock(Emailer.class);

      Capture rCap = new Capture();

      EasyMock.expect(mockEmailer.sendEmail(
          EasyMock.capture(rCap), ...)).andReturn(true);

      EasyMock.replay(mockEmailer);

      Foo myFoo = new Foo(mockEmailer);
      myFoo.doSomethingCool(false);

      EasyMock.verify(mockEmailer);

      // actual Recipient instance returned here
      Recipient recipient = rCap.getValue();
      
      Assert.assertEquals(recipient.getName(), "noreply");
      Assert.assertEquals(recipient.getEmail(), "noone@part.net");
    }
  }
}
Using a Capture has none of the requirements/drawback/quirks as the first approach and is certainly no harder to use.

Links

4 comments:

Anonymous said...

Great thanks Frank! Your post is incredibly helpful.

Unknown said...

Thanks for the post Frank, it was quite helpful!

Anonymous said...

Really nice - I've been using EasyMock for about 2 years, but these tips gave me a lot of new insight. Thanks for posting :)

Himank Nagpal said...

Thanks a lot. Very helpful.