The current recipe will be about stubbing a static method in order to properly verify the behavior of the system under test. Unfortunately, Mockito can't stub static methods, and that's why we will use PowerMock to do that.
I'd like to yet again remind you 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 static methods. PowerMock can come in hand when dealing with 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's static methods twice to get a tax factor for the given person and then calculates a mean value for these two results, as follows:
public class MeanTaxFactorCalculator { public double calculateTaxFactorFor(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 checks what country the person is from, and depending on that piece of information, it returns a proper tax factor (for readability purposes, we will not go into any details regarding this). Note that TaxFactorFetcher.getTaxFactorFor(...)
is a static method.
To use PowerMock with JUnit to stub a static method, you have to perform the following steps:
@RunWith(PowerMockRunner.class)
.@PrepareForTest
annotation (in the case of our scenario, this would be @PrepareForTest(TaxFactorFetcher .class)
since TaxFactorFetcher
has a static method that we want to stub).PowerMockito.mockStatic(...)
method to start the stubbing of static methods in the class.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 more 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 { MeanTaxFactorCalculator systemUnderTest = new MeanTaxFactorCalculator(); @Test public void should_calculate_tax_factor_for_a_player_with_undefined_country() { // given double expectedMeanTaxFactor = 10; mockStatic(TaxFactorFetcher.class); given(TaxFactorFetcher.getTaxFactorFor(any(Person.class))).willReturn(5.5, 14.5); // when double taxFactorForPerson = systemUnderTest.calculateTaxFactorFor(new Person()); // then then(taxFactorForPerson).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).PowerMockito.mockStatic(...)
method to start the stubbing of static methods in the class.Let's take a look at the TestNG test which will verify whether the tax factor is properly calculated (refer to the introduction to the analogous JUnit example, as discussed earlier, in terms of the BDDMockito
and BDDAssertions
usage):
@PrepareForTest(TaxFactorFetcher.class) public class MeanTaxFactorCalculatorTestNgTest extends PowerMockTestCase { MeanTaxFactorCalculator systemUnderTest = new MeanTaxFactorCalculator(); @Test public void should_calculate_tax_factor_for_a_player_with_undefined_country() { // given double expectedMeanTaxFactor = 10; mockStatic(TaxFactorFetcher.class); given(TaxFactorFetcher.getTaxFactorFor(any(Person.class))).willReturn(5.5, 14.5); // when double taxFactorForPerson = systemUnderTest.calculateTaxFactorFor(new Person()); // then then(taxFactorForPerson).isEqualTo(expectedMeanTaxFactor); } @ObjectFactory public IObjectFactory getObjectFactory() { return new PowerMockObjectFactory(); } }
In the majority of cases, if working on a well-written codebase, you should not need to use PowerMock at all. There are cases, however, when dealing with legacy code or third-party dependencies that you would like to mock where using PowerMock can be handy.
The best approach when using PowerMock is to make it a mean to refactor your codebase into the 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 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 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.144.93.141