Русский English Tags View Sergey Zolotaryov's profile on LinkedIn Sign-in
Mocking enums in Java to increase code coverage
Permanent link 17-12-2020 anydoby java

I know, it's tough to test for impossible situations. Especially testing the default case of your switch :) like this one.


switch (animal) {
  case: CAT:
    // do cat stuff
    break;
  case: DOG:
    // do dog stuff
    break;
  default:
    throw new IllegalArgumentException("Dunno what to do");
}

Imagine someone introduces a new enum that's not handled and forgets to add a new case.

Unfortunately Mockito does not allow creating a mock for enum, so here comes Powermock. I'm not a big fan of mocks and especially those that mock static methods, I think it's a bad smell. However mocking enums for testing illegal or default cases is just that single spot where it makes sense.

So to begin with let's add these dependencies to our project:


        <powermock.version>2.0.9</powermock.version>

        .....

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito2</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>

Then we need to mock enum before the test class is loaded. It's a bit tricky and involves messing with JDK internals. Different JDK versions have different names for enum values constant within the Enum class.


    @BeforeClass
    public static void prepareEnum() {
        int optionsLength = Animal.values().length;
        Animal[] newAnimals = new Animal[optionsLength + 1];
        System.arraycopy(Animal.values(), 0, newAnimals, 0, optionsLength);
        strangeAnimal = PowerMockito.mock(Animal.class);
        Whitebox.setInternalState(strangeAnimal, "name", Integer.toString(optionsLength));
        Whitebox.setInternalState(strangeAnimal, "ordinal", optionsLength);
        newAnimals[optionsLength] = strangeAnimal;

        try {
            // works in eclipse
            Whitebox.setInternalState(Animal.class, "ENUM$VALUES", newAnimals);
        } catch (Exception e) {
            // some JDK's put these in different field
            Whitebox.setInternalState(Animal.class, "$VALUES", newAnimals);
        }
    }

In the code above we create a mock enum with the help of bytecode manipulation and then replace values array in our enum class (in order for the switch to work correctly). Different JDK's have different name for this array. Maybe your will have yet another one :)

You might also want to write separate test classes for "regular" enum values and the illegal one, since if you only test normal cases in the "powered" unit tests the normal enum class will have zero coverage (after all Jacoco will see reference to the generated class).

Here's the link to a Github project with this exercise.

Add a comment

Previous article How to run your CXF application in WebSphere