EasyMock Examples 7

Posted by wsargent Mon, 25 Jun 2007 05:41:00 GMT

I’ve played with EasyMock for a while now, and I think I get it. I tend to see more from examples, so this is my interpretation of the EasyMock documentation.

Say that you have a class:

public class CommentService
{
   private CommentDao commentDao;
   private CommentDao getCommentDao() { return commentDao; }
   private void setCommentDao(CommentDao commentDao) { this.commentDao = commentDao; }

/**

* Adds a comment to the database.
*/

public void addComment(String name, String message) {

    Comment comment = new Comment();
    comment.setName(name);
    comment.setMessage(message);
    CommentDao dao = getCommentDao();
    dao.save(comment);

} }

There are several things that you want to happen in the unit test for the code. You want to make sure that dao.save is called. You want to check that the comment exists, and that it has the name and message that you passed in.

With all of that, the unit test looks like this:

public class CommentServiceTest
{

private CommentService commentService;
private CommentDao mockDao;

@Before
 public void setUp()
 {
     commentService = new CommentService();
     mockDao = EasyMock.createMock(CommentDao.class);
     commentService.setCommentDao(mockDao);
 }

@Test
public void testAddComment()
{   
      // We expect this method to be called when we call commentService.addComment()
      // and we don't care about the object that gets passed in...
      mockDao.save(EasyMock.anyObject());

      // Okay, we're done setting the mock up.
      EasyMock.replay(mockDao);

      // Run the method.
      commentService.addComment(name, message);

      // Check that the mock's happy with what happened between it and the service.
      EasyMock.verify(mockDao);
}

}

So that’s the simple case. What about situations where you’re relying on a context object being passed in, and you get your services from there?

You have two layers of lookup:

@Test
public void testApplicationContextLookup()
{

ApplicationContext mockContext = EasyMock.createMock(ApplicationContext.class);
ExampleService mockService = EasyMock.createMock(ExampleService.class);

// Here we DO care about what gets passed back:
EasyMock.expect(mockContext.getBean("exampleBean")).andReturn(mockService);
mockService.doStuff();

EasyMock.replay(mockContext);
EasyMock.replay(mockService);

// etc...

}

This works for interfaces. But what if you want to mock out classes?

Well, then you need the EasyMock Class Extension library. This is basically the same interface as the other library, the difference being that you need CGLIB to fill in a mock proxy class for you.

@Test
public void testHashtable() {

Hashtable hashTable = createMock(Hashtable.class);

Object key = anyObject();
Object value = anyObject();
expect(hashTable.put(key, value)).andReturn(value);

replay(hashTable);
callTheHashTable(hashTable);

verify(hashTable);

}

Okay, so we can do classes. What about other stuff? What if we only want to mock out a particular method in a class? Well, we can do that as well.

Hashtable hashTable = createMock(Hashtable.class, new Method[] { Hashtable.class.getMethod("put", Object.class, Object.class) } );

What if we don’t know what kind of object we want to return, or need to do some logic to construct a big hairy object? Use IAnswer:

expect(service.getSystemTime()).andAnswer(new IAnswer() {

  public Object answer() throws Throwable
     long setTime = 0; // return a custom time back based on some algorithm...
     return new Long(setTime);
  }

});

What if we want to verify that mock1 is called before mock2? We have to break down using a IMocksControl object.

@Test
public void testMockOrder() {

IMocksControl control = createStrictControl();

List mockList = control.createMock(List.class);
Map mockMap = control.createMock(Map.class);

// Set up the expected calls...

replay(mockList, mockMap);

// Make sure "list stuff" is done before "map stuff"
fixture.doStuff(mockList, mockMap);
verify(mockList, mockMap);

}

This does not cover everything, of course. In particular, it doesn’t cover some of the stranger edge cases (static methods, constructors, finalizers) that code may be nestled in. Also, private and final methods / classes are not covered at all by EasyMock.