Creating mocks of enums with PowerMock
Believe it or not, in some legacy systems you can find solutions where the business logic is implemented inside an enum. What we will discuss here is how to mock and stub (you will learn about stubbing more in Chapter 4, Stubbing Behavior of Mocks) an enum using PowerMock (since it's impossible to do it in Mockito). The PowerMock library setup has been described in the previous recipe, so we'll skip it. I will, however, repeat that the best outcome of using the PowerMock library would be to use it as a means to refactor the code, and, at the end of the day, remove the PowerMock dependency from the system since it is no longer needed.
Getting ready
Let's assume that we have the following enum containing business logic:
public enum Country implements TaxRateCalculator { POLAND { @Override public double calculateTaxFactorFor(Person person) { return new PolishWebService().doLongOperation(person); } }, OTHER { @Override public double calculateTaxFactorFor(Person person) { return new OtherWebService().doLongOperation(person); } }; public static Country fromCountryName(String countryName){ if(POLAND.name().equalsIgnoreCase(countryName)){ return POLAND; } return OTHER; } }
The system under test changes in a way that it can use the enum to perform computations. The enum is chosen based on the person's country name via the execution of the enum's fromCountry(...)
static method. Have a look at the following code:
public class TaxFactorCalculator { public static final double INVALID_TAX_FACTOR = -1; public double calculateTaxFactorFor(Person person) { Country country = Country.fromCountryName(person.getCountryName()); try { return country.calculateTaxFactorFor(person); } catch (Exception e) { System.err.printf("Exception [%s] occurred while trying to calculate tax for person [%s]%n", e, person.getName()); return INVALID_TAX_FACTOR; } } }
How to do it...
To mock an enum using PowerMock, you have to perform the following steps:
- Set up PowerMock for your test runner (please refer to the How to do it... section of the previous recipe for JUnit and TestNG).
- Mock it like any other final class, since enum is a type of a final class (please refer to the previous recipe on how to mock a final class).
The following code shows a JUnit test (for a TestNG setup, please refer to the previous recipe, Creating mocks of final classes with PowerMock) that verifies whether the system under test properly calculates the tax factor. The mockStatic(...)
method is statically imported from PowerMockito and is used for stubbing static methods. Don't worry if you don't entirely understand the concept of stubbing, because you can learn more about it in Chapter 4, Stubbing Behavior of Mocks.
As for the test itself, please remember that I'm using the BDDMockito.given(...)
and AssertJ's BDDAssertions.then(...)
static methods. Check out Chapter 7, Verifying Behavior with Object Matchers, on how to work with AssertJ or how to do the same with Hamcrest's assertThat(...)
. Have a look at the following code:
@RunWith(PowerMockRunner.class) @PrepareForTest(Country.class) public class TaxFactorCalculatorTest { static final double TAX_FACTOR = 10000; @Mock Country country; @InjectMocks TaxFactorCalculator systemUnderTest; @Test public void should_calculate_tax_factor() { // given mockStatic(Country.class); given(Country.fromCountryName(anyString())).willReturn(country); given(country.calculateTaxFactorFor(any(Person.class))).willReturn(TAX_FACTOR); // when double taxFactorForPerson = systemUnderTest.calculateTaxFactorFor(new Person()); // then then(taxFactorForPerson).isEqualTo(TAX_FACTOR); } }
How it works...
As seen in the previous example, the internals of PowerMock go far beyond the scope of this recipe, but the overall concept is such that part of the PowerMockRunner
logic is to create a custom classloader and bytecode manipulation for the classes defined using the @PrepareForTest
annotation in order to mock them and to use these mocks with the standard Mockito API. Due to bytecode manipulations, PowerMock can ignore a series of constraints of the Java language, such as extending final classes.
See also
- Refer to the PowerMock website at https://code.google.com/p/powermock/
- Refer to Chapter 8, Refactoring with Mockito, to see how to use PowerMock to refactor legacy code