Atom Feed SITE FEED   ADD TO GOOGLE READER

EasyMock and OutOfMemoryError: PermGen space

I wrote a test using the easy-to-use EasyMock Unit-testing API:
package com.publicobject.pizzadelivery;

import junit.framework.TestCase;
import org.easymock.classextension.EasyMock;

public class PizzaDeliveryPersonTest extends TestCase {
private PizzaStore pizzaStore;
private Customer customer;
private Order order;
private Address address;
private DeliveryPerson deliveryPerson;

@Override
public void setUp() {
pizzaStore = EasyMock.createMock(PizzaStore.class);
order = EasyMock.createMock(Order.class);
address = EasyMock.createMock(Address.class);
customer = EasyMock.createMock(Customer.class);

EasyMock.expect(pizzaStore.getNextOrder())
.andReturn(order).anyTimes();
EasyMock.expect(order.getAddress())
.andReturn(address).anyTimes();
EasyMock.expect(order.getPrice())
.andReturn(new Money(17)).anyTimes();
EasyMock.expect(address.getCoordinates())
.andReturn(new GpsCoordinates(30, 40)).anyTimes();
EasyMock.expect(customer.acceptOrder(order))
.andReturn(new Money(20).anyTimes();

EasyMock.replay(pizzaStore);
EasyMock.replay(order);
EasyMock.replay(address);
EasyMock.replay(customer);
}

public void testDeliveryPerson() {
doTestDeliveryPerson(new BasicPizzaDeliveryPerson());
}

public void testDeliveryPersonWithNoGasInCar() {
BasicPizzaDeliveryPerson deliveryPerson = new BasicPizzaDeliveryPerson()
deliveryPerson.getCar().setFuel(0);
doTestDeliveryPerson(deliveryPerson);
}

public void testDeliveryPersonWhoSpeaksFrench() {
BasicPizzaDeliveryPerson deliveryPerson = new BasicPizzaDeliveryPerson()
deliveryPerson.setNativeLanguage(Language.FRENCH);
doTestDeliveryPerson(deliveryPerson);
}

public void testRealTimeDeliveryPerson() {
PizzaDeliveryPerson deliveryPerson = new RealTimePizzaDeliveryPerson()
doTestDeliveryPerson(deliveryPerson);
}

public void testMinimumWageDeliveryPerson() {
BasicPizzaDeliveryPerson deliveryPerson = new BasicPizzaDeliveryPerson()
deliveryPerson.setHourlyWage(new Money(7));
doTestDeliveryPerson(deliveryPerson);
}

public void doTestDeliveryPerson(PizzaDeliveryPerson deliveryPerson) {
assertTrue(deliveryPerson.getTotalMoney().toDollars() == 0);
deliveryPerson.setStore(pizzaStore);
deliveryPerson.runDelivery();
assertTrue(deliveryPerson.getTotalMoney().toDollars() == 20);
assertTrue(deliveryPerson.getTotalTips().toDollars() == 3);
}
}

...but when I ran it in my continuous build, it failed due to a memory leak:
java.lang.OutOfMemoryError: PermGen space

It turns out that every time I call EasyMock.createMock()*, a piece of memory is allocated that can never be reclaimed!

My hacky fix was to make all the mocked objects static. Now they only get created once, not once for each of the 5 test methods:
public class PizzaDeliveryPersonTest extends TestCase {
private static PizzaStore pizzaStore;
private static Customer customer;
private static Order order;
private static Address address;
private static DeliveryPerson deliveryPerson;

static {
pizzaStore = EasyMock.createMock(PizzaStore.class);
order = EasyMock.createMock(Order.class);
address = EasyMock.createMock(Address.class);
customer = EasyMock.createMock(Customer.class);

EasyMock.expect(pizzaStore.getNextOrder())
.andReturn(order).anyTimes();
EasyMock.expect(order.getAddress())
.andReturn(address).anyTimes();
EasyMock.expect(order.getPrice())
.andReturn(new Money(17)).anyTimes();
EasyMock.expect(address.getCoordinates())
.andReturn(new GpsCoordinates(30, 40)).anyTimes();
EasyMock.expect(customer.acceptOrder(order))
.andReturn(new Money(20).anyTimes();

EasyMock.replay(pizzaStore);
EasyMock.replay(order);
EasyMock.replay(address);
EasyMock.replay(customer);
}

...
}

It gets the job done, but not well. The disadvantages of this approach:
  • I need to use anyTimes() on my objects so their methods can be called repeatedly
  • The code is ugly
  • I still have a memory leak, it's just a bit smaller

    It would be a great fix for EasyMock if multiple calls to EasyMock.createMock(PizzaStore.class) could share the same generated class object.