In this recipe, we will stub a final method and verify the behavior of the system under test. Since Mockito can't stub methods that are final, we'll use PowerMock to do it.
Remember that it absolutely isn't good practice to use PowerMock in your well-written code. If you follow all of the SOLID principles (please refer to Chapter 2, Creating Mocks, for the explanation of each of these principles), then you should not resort to stubbing final methods. PowerMock can come in hand when dealing with the legacy code or stubbing third-party libraries (you can check Chapter 8, Refactoring with Mockito, to see how to use PowerMock to refactor the badly written code).
To use PowerMock, you have to add it to your classpath. Please check the Creating mocks of final classes with PowerMock recipe in Chapter 2, Creating Mocks, for more details on how to add PowerMock to your project.
For this recipe, our system under test will be MeanTaxFactorCalculator
, which calls a TaxFactorFetcher
object twice to get a tax factor for the given person and then calculates a mean value for those two results:
public class MeanTaxFactorCalculator { private final TaxFactorFetcher taxFactorFetcher; public MeanTaxFactorCalculator(TaxFactorFetcher taxFactorFetcher) { this.taxFactorFetcher = taxFactorFetcher; } public double calculateMeanTaxFactorFor(Person person) { double taxFactor = taxFactorFetcher.getTaxFactorFor(person); double anotherTaxFactor = taxFactorFetcher.getTaxFactorFor(person); return (taxFactor + anotherTaxFactor) / 2; } }
Let's assume that TaxFactorFetcher
is a class that returns a proper tax factor (for readability purposes, we'll omit going through its implementation since it's irrelevant for this recipe) based on the person's origin. One thing worth noting is that TaxFactorFetcher.getTaxFactorFor(...)
is a final method.
To use PowerMock with JUnit to stub a final method, you have to perform the following steps:
@RunWith(PowerMockRunner.class)
.@PrepareForTest
annotation (in the case of our scenario, it would be @PrepareForTest(TaxFactorFetcher .class)
since TaxFactorFetcher
has a final method that we want to stub). In general, the class that needs to be prepared for testing would include classes with final, private, static or native methods, classes that are final and that should be spied, and also classes that should be returned as spies on instantiation.Let's take a look at the JUnit test which will verify whether the tax factor is properly calculated (remember that I'm using BDDMockito.given(...)
and AssertJ's BDDAssertions.then(...)
static methods. Check out Chapter 7, Verifying Behavior with Object Matchers, for details on how to work with AssertJ or how to do the same with Hamcrest's assertThat(...)
):
@RunWith(PowerMockRunner.class) @PrepareForTest(TaxFactorFetcher.class) public class MeanTaxFactorCalculatorTest { @Mock TaxFactorFetcher taxFactorFetcher; @InjectMocks MeanTaxFactorCalculator systemUnderTest; @Test public void should_calculate_tax_factor_for_a_player_with_undefined_country() { // given double expectedMeanTaxFactor = 10; given(taxFactorFetcher.getTaxFactorFor(any(Person.class))).willReturn(5.5, 14.5); // when double meanTaxFactor = systemUnderTest.calculateMeanTaxFactorFor(new Person()); // then then(meanTaxFactor).isEqualTo(expectedMeanTaxFactor); } }
To use PowerMock with TestNG to create a spy for final classes, you have 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, this would be @PrepareForTest(TaxFactorFetcher .class)
since TaxFactorFetcher
has a final method that we want to stub).@PrepareForTest(TaxFactorFetcher.class) public class MeanTaxFactorCalculatorTestNgTest extends PowerMockTestCase { @Mock TaxFactorFetcher taxFactorFetcher; @InjectMocks MeanTaxFactorCalculator systemUnderTest; @Test public void should_calculate_tax_factor_for_a_player_with_undefined_country() { // given double expectedTaxFactor = 10; given(taxFactorFetcher.getTaxFactorFor(any(Person.class))).willReturn(5.5, 14.5); // when double taxFactorForPerson = systemUnderTest.calculateMeanTaxFactorFor(new Person()); // then then(taxFactorForPerson).isEqualTo(expectedTaxFactor); } @ObjectFactory public IObjectFactory getObjectFactory() { return new PowerMockObjectFactory(); } }
In the majority of cases, if working on a well-written code base, you should not have the need to use PowerMock at all. There are cases, however, when dealing with the legacy code or third-party dependencies that you would like to mock where using PowerMock comes in handy.
The best approach with using PowerMock is to make it a mean to refactor your codebase into one that doesn't need any of PowerMock's tweaking.
The internals of PowerMock go far beyond the scope of this recipe. However, the overall concept is such that part of the PowerMockRunner's 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 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.
3.133.133.61