Bram Smeets Blog

 

Blog on Spring and DWR. AJAX with Spring made easy!

EasyMock2 in a Nutshell

Filed under: Java — Bram Smeets at 1:30 pm on Wednesday, October 5, 2005

The essential tool for unit testing, EasyMock, has come to a new level. This blog entry introduces the new EasyMock2 release.
While it is still in pre-release stage, I do recommend using EasyMock2 for all your Java 5 projects.

To quote Tammo Freese (Project Founder) from the EasyMock project:

‘I plan no modifications other than bug fixes from this point.’

What’s new

  1. No more need for MockControl objects
  2. Build in argument matchers for most common argument matching (a lot less nuisance of having to write your own ArgumentMatcher)

Step-by-step example

Let’s look at a step-by-step example of using EasyMock2:

  1. Put the following line in your imports:

    import static org.easymock.EasyMock.*;

    Note the static import, this imports all static members (methods in this case) from the EasyMock class.
    More on static imports here: http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html.

  2. Create a mock object using the following line (probably in your setup() method):

    myManager = createMock(MyManager.class);

    Note 1. It is still only possible to mock interfaces and NOT classes. However, using EasyMock Class Extension it is possible to mock classes.
    Note 2. The createMock() method is a static method on the EasyMock class. It is still possible to use the methods createStrictMock() and createNiceMock() to respectively create a mock which also verifies the order in which the methods are called and a mock that allows all method calls and returns appropriate empty values by default.
    Important: do not forget to set the mocked object on the class under testing.

  3. Specify behavior for the mock object. This example assumes we expect the method isDebug() to be called on our manager six times and returning false on each occasion:

    expect(myManager.isDebug()).andReturn(false).times(6);

    Note 1. Apart from andReturn(), you could also use andThrow() to throw an exception. You can also use andStubReturn() and andStubThrow() to specify default behavior.
    Note2. In EasyMock2 it is easy to specify the number of times you expect the method to be called (in the example the times(6) call). Next to the times(int) method, there are a number of other convenience methods:

    times(int count)
    times(int min, int max) - expect between min and max calls
    atLeastOnce() - expect the method to be called at least once
    anyTimes() - expect an unrestricted number of calls

  4. Set the mock in the replay state and verify after testing the class under test, similar to calling replay and verify on the MockControl object in the previous EasyMock version:

    replay(myManager);<br />
    classUnderTest.someMethod();<br />
    verify(myManager);

Best new feature

Ok, so far so good, but I left out the best new feature (in my opinion).

Remember a method being called with unknown or different arguments on each call. You had to write your own argument matcher. No more……
Now, for example you can do the following:

myManager.someMethod(eq(“anArgument”), isA(Method.class), notNull());

Note 1. The first argument is equivalent to just calling myManager.someMethod(“anArgument” ….
Note 2. The second argument specifies that it expects an instance of the Method class
Note 3. The last argument is expected to not be null
Note 4. Either all arguments of a call have to be given by specifying argument matchers, or none

The list of build in argument matchers is pretty extensive, but ofcourse it is still possible to write your own ArgumentMatcher ;-)
Below is a list of all build in argument matchers (copied from the EasyMock2 documentation).

eq(X value)
    Matches if the actual value is equals the expected value. Available for all primitive types and for objects.
anyBoolean(), anyByte(), anyChar(), anyDouble(), anyFloat(), anyInt(), anyLong(), anyObject(), anyShort()
    Matches any value. Available for all primitive types and for objects.
eq(X value, X delta)
    Matches if the actual value is equal to the given value allowing the given delta. Available for float and double.
aryEq(X value)
    Matches if the actual value is equal to the given value according to Arrays.equals(). Available for primitive and object arrays.
isNull()
    Matches if the actual value is null. Available for objects.
notNull()
    Matches if the actual value is not null. Available for objects.
same(X value)
    Matches if the actual value is the same as the given value. Available for objects.
isA(Class clazz)
    Matches if the actual value is an instance of the given class, or if it is in instance of a class that extends or implements the given class. Available for objects.
lt(X value), leq(X value), geq(X value), gt(X value)
    Matches if the actual value is less than/less or equal/greater or equal/greater than the given value. Available for all numeric primitive types.

Hope this blog entry will help you improve your unit testing! It has definitely reduced the amount of mock code in the tests that I have refactored to EasyMock2. Hopefully the final version will be released very soon.

9 Comments »

Comment by Herryanto Siatono

January 20, 2006 @ 1:16 pm

The new features look very much similar to JMock, anyway those new methods will come in handy.

Comment by Alexxzwc

February 11, 2007 @ 2:21 am

Hello, my name is Alex, i’m a newbie here. I really do like your resource and really interested in things you discuss here, also would like to enter your community, hope it is possible:-) Cya around, best regards, Alex!

Comment by JJ

February 15, 2007 @ 11:37 pm

Hi, I’m trying to use EasyMock as you suggested. Here is what my tests look like:

I’m getting null in my assertEquals(”person1″, person.getFirstname());

public class PersonManagerIntegrationTests extends AbstractJpaTests {

private PersonManager personManager = createMock(PersonManager.class);
private Person person;
private List personList;

protected String[] getConfigLocations() {
return new String[] {”classpath:/*/applicationContext.xml”};
}

protected void onSetUpInTransaction() throws Exception {
jdbcTemplate.execute(”insert into person (id, firstname, lastname, dob, job) values (2, ‘person1′, ‘name1′, ‘01/01/2001′, ‘IT’)”);
jdbcTemplate.execute(”insert into person (id, firstname, lastname, dob, job) values (3, ‘person2′, ‘name2′, ‘01/01/2001′, ‘IT’)”);
jdbcTemplate.execute(”insert into person (firstname, lastname, dob, job) values (’person3′, ‘name3′, ‘01/01/2001′, ‘IT’)”);
jdbcTemplate.execute(”insert into person (firstname, lastname, dob, job) values (’person4′, ‘name4′, ‘01/01/2001′, ‘IT’)”);
jdbcTemplate.execute(”insert into person (firstname, lastname, dob, job) values (’person5′, ‘name5′, ‘01/01/2001′, ‘IT’)”);
}

public void testFindById() {
expect(personManager.findById(”2″)).andReturn(new Person()).times(1);
replay(personManager);
person = personManager.findById(”2″);

assertNotNull(person);
assertEquals(”person1″, person.getFirstname());
verify(personManager);
}

Comment by Bram Smeets

February 18, 2007 @ 11:07 pm

Hi JJ,
It seems to me that you are testing the wrong thing. You typically want to test the real PersonManager class and use EasyMock to mimic the behavior of collaborator objects.

In this case, you are using an integration test (AbstractJpaTests) to test your manager. You insert data into the database in the onSetupInTransaction method. So it seems to me that you do NOT want to use EasyMock to create a mock object.

If you only want to test the implementation of the manager (so without the integration with the database) then you would want to use EasyMock.

In that case, you want to create a mock version of for instance the DAO that the manager uses to retrieve the data. So you do NOT create a mock for the object you want to test, but for the collaborator objects.

I hope this helps,
Bram

Comment by JJ

February 20, 2007 @ 1:00 am

Hi Bram,

Thanks for the response. I’m still not clear on just testing the PersonManager class. I’m getting Unexpected method call findById(”1″) error in my testSavePerson(). I can’t test removePerson, it’s saying The method expect(T) in the type EasyMock is not applicable for the arguments(void). Am I doing something wrong?

public class PersonManagerTest extends TestCase {
private PersonDao personDaoMock;
private PersonManager personManagerMock;
private Person person = null;

protected void setUp() throws Exception {
super.setUp();
personDaoMock = createMock(PersonDao.class);
personManagerMock = createMock(PersonManager.class);
}

public void testFindById() throws Exception {
expect(personManagerMock.findById(”1″)).andReturn(new Person());
replay(personManagerMock);
person = personManagerMock.findById(”1″);
verify(personManagerMock);
assertTrue(person != null);
}

public void testSavePerson() throws Exception {
person = new Person();
person.setFirstname(”John”);
person.setLastname(”Doe”);
person.setDob(new Date(”12/12/2000″));
person.setJob(”IT”);
expect(personManagerMock.savePerson(person)).andReturn(person);
replay(personManagerMock);
person = personManagerMock.savePerson(person);
verify(personManagerMock);

reset();
expect(personManagerMock.findById(”1″)).andReturn(new Person());
replay(personManagerMock);
person = personManagerMock.findById(”1″);
verify(personManagerMock);
assertTrue(person != null);
assertEquals(person.getFirstname(), “John”);

}

public void testRemovePerson() throws Exception {
expect(personManagerMock.removePerson(”1″));
}

Comment by Bram Smeets

February 20, 2007 @ 12:56 pm

You are still creating a mock object for the PersonManager. You should change your setUp() method to something like:

protected void setUp() throws Exception {
personDaoMock = createMock(PersonDao.class);
personManager = new PersonManagerImpl();
personManager.setPersonDao(personDaoMock);
}

And then record the behavior on the DAO and then in the test call the PersonManager methods (which will subsequently call the DAO.

Comment by Kiran

October 9, 2007 @ 8:08 am

What about number of times for a void method call?

Comment by Bram Smeets

October 9, 2007 @ 4:15 pm

I’m not 100% sure, but I think you can use expectLastCall().times(6); or something similar.

Comment by KV

January 9, 2008 @ 9:34 pm

Question. I posted a Q in the easymock forum. Got a response, but I am still stuck.

bottom line, I have a private method call from a public method. Easy mock is failing with an assertion error. How am I supposed to handle this

http://tech.groups.yahoo.com/group/easymock/message/1104

RSS feed for comments on this post. TrackBack URI

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>