Stubbing static methods with PowerMock

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).

Getting ready

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.

How to do it...

To use PowerMock with JUnit to stub a static method, you have to perform the following steps:

  1. Annotate your test class with @RunWith(PowerMockRunner.class).
  2. Provide all of the classes that need to be prepared for testing (most likely, bytecode manipulated classes) in the @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).
  3. Before stubbing a static method, you have to call the PowerMockito.mockStatic(...) method to start the stubbing of static methods in the class.
  4. Stub static methods in a standard way as you would while using objects.
  5. Use Mockito annotations in a standard way to set up test doubles.

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:

  1. Make your class extend the PowerMockTestCase class.
  2. Implement a method annotated with the @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).
  3. Provide all of the classes that need to be prepared for testing (most likely, bytecode manipulated classes) in the @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).
  4. Before stubbing a static method, you have to call the PowerMockito.mockStatic(...) method to start the stubbing of static methods in the class.
  5. Stub static methods in a standard way as you would while using objects.
  6. Use Mockito annotations in a standard way to set up test doubles.

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();
    }
  
}

Note

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.

How it works...

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.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.93.141