Before going into the details of this recipe, if you haven't already done so, please read the Creating mocks of final classes with PowerMock recipe of Chapter 2, Creating Mocks. PowerMock is a powerful (thus dangerous) tool, that in the hands of an inexperienced developer, can lead to the creation of really bad test and production code.
Why would you want to use PowerMock? Mockito can't create mocks for classes that are final. The same problem exists when trying to create spies. If you have a properly written test-driven code, you shouldn't have the need to use either spies or partial mocks, nor have PowerMock in your project. If you need to use PowerMock to create spies of final classes, do it only as a last resort in order to refactor your code, and at the end remove PowerMock dependencies and only use Mockito. Refer to Chapter 8, Refactoring with Mockito, for examples of how to use PowerMock as a mean to refactor your code (and at the end of the day, remove PowerMock from your classpath).
Speaking of classpaths, in order to use PowerMock with Mockito, you need to add it to your classpath. Please refer to the Creating Mocks of final classes with PowerMock recipe of Chapter 2, Creating Mocks, to know how to add PowerMock to your project.
Our system under test will be a class whose responsibility is to calculate the tax factor for a given person. It interacts with TaxService
, which happens to be a final class. (We'll omit its implementation since it's irrelevant for this recipe. The important thing is to remember that it's a final class.) Have a look at the following code:
public class TaxFactorProcessor { public static final double INVALID_TAX_FACTOR = -1; private final TaxService taxService; public TaxFactorProcessor(TaxService taxService) { this.taxService = taxService; } public double processTaxFactorFor(Person person) { try { double taxFactor = taxService.calculateTaxFactorFor(person); taxService.updateTaxData(taxFactor, person); return taxFactor; } catch (Exception e) { System.err.printf("Exception [%s] occurred while trying to calculate tax factor for person [%s]%n", e, person.getName()); return INVALID_TAX_FACTOR; } } }
To use PowerMock with JUnit to create a spy for final classes, you need to perform the following steps:
@RunWith(PowerMockRunner.class)
.@PrepareForTest
annotation (in the case of our scenario, it would be @PrepareForTest(TaxService.class)
since TaxService
is a final class). In general, the classes that need to be prepared for testing would include those with final, private, static, or native methods. These are classes that are final and should be spied on, and are also classes that should be returned as spies on instantiation.@Spy
annotation and instantiate that object (it differs from the standard Mockito approach, where if the spy has a default constructor, you wouldn't need to instantiate it).Let's take a look at the JUnit test which will verify whether the tax factor is properly calculated (remember that I'm using the BDDMockito.given(...)
and AssertJ's BDDAssertions.then(...)
static methods. Refer to Chapter 7, Verifying Behavior with Object Matchers, to know 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 TaxFactorProcessorTest { static final double TAX_FACTOR = 10000; @Spy TaxService taxService = new TaxService(); @InjectMocks TaxFactorProcessor systemUnderTest; @Test public void should_return_default_tax_factor_for_person_from_undefined_country() { // given doReturn(TAX_FACTOR).when(taxService).calculateTaxFactorFor(Mockito.any(Person.class)); // when double taxFactorForPerson = systemUnderTest.processTaxFactorFor(new Person()); // then then(taxFactorForPerson).isEqualTo(TAX_FACTOR); } }
To use PowerMock with TestNG to create a spy for final classes, you need to perform the following steps:
PowerMockTestCase
class.@ObjectFactory
annotation that returns an instance of the PowerMockObjectFactory
class (this object factory will be used for the creation of all object instances in the test).@PrepareForTest
annotation (in the case of our scenario, it would be @PrepareForTest(TaxService.class)
since TaxService
is a final class). This includes classes with final, private, static, or native methods. These are classes that are final and that should be spied on; and are also classes that should return a spy on object instantiation.@Spy
annotation and instantiate that object (it differs from the standard Mockito approach, where if the spy has a default constructor, you wouldn't need to instantiate it).Let's take a look at the TestNG test that will verify whether the tax factor is properly calculated (refer to the introduction to the preceding analogous JUnit example for more information on BDDMockito
and BDDAssertions
usage):
@PrepareForTest(TaxService.class) public class TaxFactorProcessorTestNgTest extends PowerMockTestCase { static final double TAX_FACTOR = 10000; @Spy TaxService taxService = new TaxService(); @InjectMocks TaxFactorProcessor systemUnderTest; @Test public void should_return_default_tax_factor_for_person_from_undefined_country() { // given doReturn(TAX_FACTOR).when(taxService).calculateTaxFactorFor(Mockito.any(Person.class)); // when double taxFactorForPerson = systemUnderTest.processTaxFactorFor(new Person()); // then then(taxFactorForPerson).isEqualTo(TAX_FACTOR); } @ObjectFactory public IObjectFactory getObjectFactory() { return new PowerMockObjectFactory(); } }
The internals of PowerMock go far beyond the scope of this recipe, but the overall concept is such that a part of the PowerMockRunner
logic is to create a custom classloader and byte-code 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 byte-code manipulations, PowerMock can ignore a series of constraints of the Java language, such as extending final classes.
18.116.65.130