Creating mocks of final classes with PowerMock
Although these situations should not take place in a well-written and test-driven system, there are cases in which it is necessary to mock some legacy code or third-party libraries that are impossible to be mocked only by means of Mockito. In this recipe, we will see how to deal with those abnormal situations using the PowerMock
library. Remember, however, that this tool is extremely powerful and the very need to use it suggests that something may really be wrong with your code. The best outcome of using this library would be to use it as means to refactor the bad code and, at the end of the day, remove the PowerMock dependency from the system since it is no longer needed.
Getting ready
In order to use PowerMock with Mockito, you need to include the following library in your classpath. If you are using a dependency management system such as Gradle or Maven, you can add it to the code as follows:
The dependency definition for Gradle is as follows:
testCompile 'org.powermock:powermock-api-mockito:1.5.2'
The dependency definition for Maven is as follows:
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>1.5.2</version> <scope>test</scope> </dependency>
Now, depending on the integration with JUnit or TestNG, there is an additional JAR file needed.
If you are using JUnit, then provide either of the following dependencies:
The dependency definition for Gradle is as follows:
testCompile 'org.powermock:powermock-module-junit4:1.5.2'
The dependency definition for Maven is as follows:
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.5.2</version> <scope>test</scope> </dependency>
Assuming that you are using TestNG, configure your dependencies as follows:
The dependency definition for Gradle is as follows:
testCompile 'org.powermock:powermock-module-testng:1.5.2'
The dependency definition for Maven is as follows:
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-testng</artifactId> <version>1.5.2</version> <scope>test</scope> </dependency>
You can also download the JAR files from PowerMock's website at https://code.google.com/p/powermock/wiki/Downloads?tm=2.
Our system under test will be a class whose responsibility it is to calculate the tax factor for the current person. It interacts with TaxService
, which happens to be a final class (we'll omit its implementation since it's irrelevant for this recipe; what's important to remember is that it's a final class). Have a look at the following code:
public class TaxFactorCalculator { public static final double INVALID_TAX_FACTOR = -1; private final TaxService taxService; public TaxFactorCalculator(TaxService taxService) { this.taxService = taxService; } public double calculateTaxFactorFor(Person person) { try { return taxService.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 use PowerMock with JUnit, you have to perform the following steps:
- Annotate your test class with
@RunWith(PowerMockRunner.class)
. - Provide all the classes that need to be prepared for testing (most likely bytecode manipulated) in the
@PrepareForTest
annotation (in the case of our scenario, it would be@PrepareForTest(TaxService.class)
sinceTaxService
is a final class). In general, classes that need to be prepared for testing will include classes with final, private, static or native methods; classes that are final and that should be mocked; and also classes that should be returned as mocks on instantiation.
Let's take a look at the JUnit test that will verify whether the tax factor is properly calculated (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(TaxService.class) public class TaxFactorCalculatorTest { static final double TAX_FACTOR = 10000; @Mock TaxService taxService; @InjectMocks TaxFactorCalculator systemUnderTest; @Test public void should_calculate_tax_factor() { // given given(taxService.calculateTaxFactorFor(Mockito.any(Person.class))).willReturn(TAX_FACTOR); // when double taxFactorForPerson = systemUnderTest.calculateTaxFactorFor(new Person()); // then then(taxFactorForPerson).isEqualTo(TAX_FACTOR); } }
To use PowerMock with TestNG, you have to perform the following steps:
- Make your class extend the
PowerMockTestCase
class. - Implement a method annotated with the
@ObjectFactory
annotation that returns an instance of thePowerMockObjectFactory
class (this object factory will be used for the creation of all object instances in the test). - Provide all the classes that need to be prepared for testing (most likely bytecode manipulated) in the
@PrepareForTest
annotation (in the case of our scenario, it would be@PrepareForTest(TaxService.class)
sinceTaxService
is a final class). This includes classes with final, private, static, or native methods; classes that are final and that should be mocked; and also classes that should be returned a mock on object instantiation.
Let's take a look at the following JUnit test that will verify whether the tax factor is properly calculated (consult the introduction to the analogous JUnit example discussed earlier in terms of BDDMockito
and BDDAssertions
usage):
@PrepareForTest(TaxService.class) public class TaxFactorCalculatorTestNgTest extends PowerMockTestCase { static final double TAX_FACTOR = 10000; @Mock TaxService taxService; @InjectMocks TaxFactorCalculator systemUnderTest; @Test public void should_calculate_tax_factor() { // given given(taxService.calculateTaxFactorFor(any(Person.class))).willReturn(TAX_FACTOR); // when double taxFactorForPerson = systemUnderTest.calculateTaxFactorFor(new Person()); // then then(taxFactorForPerson).isEqualTo(TAX_FACTOR); } @ObjectFactory public IObjectFactory getObjectFactory() { return new PowerMockObjectFactory(); } }
How it works...
The internals of PowerMock go far beyond the scope of this recipe but the overall concept is that part of the logic of PowerMockRunner
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 bad code