Mocking Java enums with jMock is both trivial and impossible

October 02, 2013
I recently ran into a situation where I needed to test some code that used an enum to select between various strategies. As a toy example of this, we’ll use Accounts which have a list of investments and a Strategy which defines what investments are acceptable.
class Account {
  private Strategy strategy;
  private Map<String, Double> investments;

  boolean addInvestment(String symbol, double fraction, double riskiness) {
    if (strategy.isRiskAcceptable(fraction, riskiness)) {
      investments.put(symbol, fraction);
      return true;
    }
    return false;
  }
}
Code language: Java (java)
Strategy is a Java 5 enum that provides a method to define what’s acceptable.
enum Strategy {
  AGGRESIVE {
    @Override
    boolean isRiskAcceptable(double percentOfAssets, double riskiness) {
      return percentOfAssets * riskiness < 0.25;
    }
  },
  CONSERVATIVE {
    @Override
    boolean isRiskAcceptable(double percentOfAssets, double riskiness) {
      return riskiness < 0.25;
    }
  };
  abstract boolean isRiskAcceptable(double percentOfAssets, double riskiness);
}
Code language: Java (java)
This gives nice simple code. Strategy is also easy to test — we can test each of the enum values separately as appropriate to those strategies. Here’s one of the tests.
public class StrategyTest {
  @Test
  public void testHighRiskLowFraction_okayForRisky() {
    assertTrue(Strategy.AGGRESIVE.isRiskAcceptable(0.1, 0.75));
    assertTrue(Strategy.AGGRESIVE.isRiskAcceptable(0.2, 0.75));
    assertTrue(Strategy.AGGRESIVE.isRiskAcceptable(0.2, 0.5));
  }
}
Code language: Java (java)
Testing the account is a little bit harder. We don’t want to implicitly test Strategy while testing the account. What we want to test is: assuming Strategy works correctly, what’s the correct behavior of account. And I’m sure by now you’re already typing the double braces of a jMock Expectations block, which is exactly what we need. The Strategy takes some input, and gives back a result. We want to know that:
  1. Account is passing the parameters to isRiskAcceptable correctly, and,
  2. Account is behaving correctly based on what isRiskAcceptable returns.
For that, we need a mock instance of Strategy. Mockery defaults to only being able to mock interfaces, so I need to install the ClassImposteriser. With this, jMock uses Objenesis to instantiate the objects rather than using CGLib to create a class implementing the mocked interface. The fact that the enum class extends Enum doesn’t matter to jMock.
public class AccountTest {
  Mockery mockery;
  Strategy mockStrategy;
  Account account;

  @Before
  public void before() {
    mockery = new Mockery();
    mockery.setImposteriser(ClassImposteriser.INSTANCE);
    mockStrategy = mockery.mock(Strategy.class);
    account = new Account(mockStrategy);
  }

  @Test
  public void testAcceptableRisk_getsAdded() {
    mockery.checking(new Expectations() {{
      oneOf(mockStrategy).isRiskAcceptable(0.1, 0.2);
      will(returnValue(true));
    }});
    account.addInvestment("BND", 0.1, 0.2);
    mockery.assertIsSatisfied();
    assertEquals(1, account.getInvestments().size());
  }

  @Test
  public void testUnacceptableRisk_doesntGetAdded() {
    mockery.checking(new Expectations() {{
      oneOf(mockStrategy).isRiskAcceptable(0.1, 0.2);
      will(returnValue(false));
    }});
    account.addInvestment("VWO", 0.1, 0.2);
    mockery.assertIsSatisfied();
    assertTrue(account.getInvestments().isEmpty());
  }

}
Code language: Java (java)
As you can see in AccountTest, this lets me test Account’s functionality in isolation. Because I’m supplying a mock Strategy, this test won’t break when I change the rules in Strategy. In fact, I didn’t make any attempt to match the parameters to the results; I just supply different values to make sure I didn’t mix up the parameters somewhere along the way and to make it easier for the next person coming along to read the intent of the test. While on the subject of jMock style, purists might argue that since isRiskAcceptable is just providing a value rather than causing any side effect, I could use allowing instead of oneOf and leave off the assertIsSatisfied. In this case, you would refer to the dynamically created instance as a stub rather than a mock.

A note on the “right” solution to this.

You can also get the same behavior by mapping your strategies using Hibernate with single table inheritance. Unfortunately, you’d still need to use the class imposteriser or open yourself up to the possibility of runtime errors when someone tries to set an unmapped instance of the interface as the strategy on your object. It avoids the issue of switch because you no longer have switch available &emdash; you need to either put the logic in the class and use method dispatch or use a visitor. This isn’t a bad solution, but it’s a heavier solution more appropriate where the strategy really captures a big chunk of functionality. Of course, there’s nothing saying that you can’t make your core object polymorphic, but this is rarely a good solution if the sub type of the objects can change.
So there we are. If you clone Wealthfront’s github repository and run AccountTest, you’ll see it passes, and if you’re skeptical, you can try changing something and watch the tests fail. We’re all done, right? Well, maybe. Unfortunately, these surreptiously created instances of the enum may stand in for some uses, but enums are still special, and you can run into problems elsewhere. One other common (well, as common as any of this stuff is) reason to want to mock an enum is to allow testing default cases in switch statements that already cover all values. For example, if I wanted to turn the above Strategy enum into a human readable description, I might do something like this:
  switch (account.getStrategy()) {
  case AGGRESIVE:
    return "aggressive high risk";
  case CONSERVATIVE:
    return "conservative low risk";
  default:
    return "unknown strategy";
  }
Code language: Java (java)
This is not so easy to test. While calling methods on enums behaves just like calling methods on any other object, but switch actually uses the ordinal value of the enum for efficiency. The mockery solution I suggest above will set an ordinal value of 0, and so follow the path for the first enum value. Using reflection to set the ordinal value (e.g. to Strategy.values().length) just makes it fail differently. In other words, we can easily create an instance of the enum, but we can’t make the static fields in the associated class aware of this instance. Solutions to this require more abuse of the JVM than I think is worth it, but those interested can see Java Specialists’ post on Hacking Enums. The reason I’m not too worried about this limitation is because we tend not to switch on enums, but instead favor using the Visitor pattern which gives the effect of type-safe Scala pattern matching on sealed case classes in Java. I’ll go into the details in a future post. The full example as a maven project is available in Wealtfront’s github.