Stubbing final methods with PowerMock

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

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

How to do it...

To use PowerMock with JUnit to stub a final 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, 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.
  3. 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 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:

  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. Use Mockito annotations in a standard way to set up test doubles.
  5. 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 in terms of the BDDMockito and BDDAssertions usage):
    @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();
        }
    }

Note

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.

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

See also

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

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