Monday, August 11, 2008

EasyMock Tip

EasyMock is a light weight Mock library used for mocking interfaces in Unit Tests. Here is a little tip on what I believe to be an undocumented best practice.

Let's say you have a Unit Test that is testing an object called CustomerController. CustomerController has a dependency on Interface CustomerDao Typically, you may see in the unit test, code that looks similar to the following:

protected void setUp() throws Exception {
    controller = new CustomerController();
    custDao = createMock(CustomerDao.class);
    controller.setCustDao(custDao);
}

public void testFindCustomers() throws Exception {
    expect(custDao.getCustomers()).andReturn(new ArrayList());
    replay(custDao);
    controller.findCustomers();
    verify(custDao);
}

As you will notice, I am specifying one mock custDao, and the system under test is the controller. This is pretty standard and straightforward use of EasyMock. Now let's see what happens when we have to add an additional dependency on the controller:

protected void setUp() throws Exception {
    controller = new CustomerController();
    custDao = createMock(CustomerDao.class);
    invoiceController = createMock(InvoiceController.class);
    controller.setCustDao(custDao);
    controller.setInvoiceController(invoiceController);
}

public void testFindCustomers() throws Exception {
    expect(custDao.getCustomers()).andReturn(new ArrayList());
    replay(custDao);
    controller.findCustomers();
    verify(custDao);
}

public void testApplyCustomerPayment() throws Exception {
    Customer customer = new Customer();
    BigDecimal paymentAmt = new BigDecimal(100);
    invoiceController.applyPayment(customer, paymentAmt);
    replay(invoiceController);
    controller.applyPayment(customer, paymentAmt);
    verify(invoiceController);
}

This also seems to be a very valid test. I would argue though that it is not complete. You are verifying that the appropriate methods are called on the objects / interfaces that you set expectations for. However, if a method is called on the custDao in the controller.applyPayment or a method is called on the invoiceController during the controller.findCustomers, without an expectation / verification the test will still pass. This may not be the desired behavior and therefore I would argue that the test is incomplete.

On my team, someone determined that a way to handle these types of situations is to create a private method on the test called replayAll and verifyAll. This included the maintaining of an array list of all of the mocks that is used within the test. While this is a valid solution, I wanted to see what other EasyMock users were doing. Here is what I found:

protected void setUp() throws Exception {
    controller = new CustomerController();
    mockControl = createControl();
    custDao = mockControl.createMock(CustomerDao.class);
    invoiceController= mockControl.createMock(InvoiceController.class);
    controller.setCustDao(custDao);
    controller.setInvoiceController(invoiceController);
}

public void testFindCustomers() throws Exception {
    expect(custDao.getCustomers()).andReturn(new ArrayList());
    mockControl.replay();
    controller.findCustomers();
    mockControl.verify();
}

public void testApplyCustomerPayment() throws Exception {
    Customer customer = new Customer();
    BigDecimal paymentAmt = new BigDecimal(100);
    invoiceController.applyPayment(customer, paymentAmt);
    mockControl.replay();
    controller.applyPayment(customer, paymentAmt);
    mockControl.verify();
}

The subtle difference to look at between these two scenarios is that the latest one uses what is known as a IMocksControl object. This IMocksControl object keeps track of what mocks were created on it so that all you would need to do in order to make sure that you are replaying and verifying all of the mocks is to call the replay and verify methods on the IMocksControl object. This ensures that all of the expectations, whether explicit or implied, are met. Thanks Tammo Freese for your help in finding this great golden nugget within the EasyMock framework.

Thursday, August 7, 2008

Barcamp Grand Rapids Episode 3 :- Revenge of the Unconference

Barcamp Grand Rapids 3 is almost here!!!

After attending the first two BarCampGR(s), I have to say that I am most excited about attending this one. Not only does each one seems to get better and better, but this year, we have had a huge amount of support from the local businesses and we have surpassed expectation of registered attendees. I am very excited to see friends that I have made from the past events, as well as making new ones.

If you are new to the BarCamp concept, please take the time to investigate it further. I will say that is unlike any other commercial conference that you have ever attended. First and foremost, the event is 100% free. Now don't get the wrong impression, since the event is going to last for two days and it is free, please don't expect that you will be forced to brown bag it for the weekend. Thanks to generous donations from local area businesses, this event will also be catered and we will be handing out schwag to boot.

This will be a great time, and I hope to see you there.....