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.
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; } } }
To mock an enum using PowerMock, you have to perform the following steps:
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); } }
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.
18.118.2.225