Stubbing object instantiation using PowerMock

In some badly written code, you can find cases in which the system under test's collaborators are not passed into the object in any way (for example, by the constructor), but the object itself instantiates them via the new operator. The best practice would be to not write like this in the first place. But let's assume that you have inherited such a code and, since we follow the boy scout rule, that you should leave the code that you've encountered in a better state than you the one in which you have found it in the first place. We have to do something about this.

The very step of the refactoring of such a scenario is presented in Chapter 8, Refactoring with Mockito. This is why, in the current recipe, we will just learn how to stub object initialization in such a way that instead of creating a new instance of an object, a mock will be returned. Unfortunately, Mockito can't perform such stubbing, and that's why we will use PowerMock to do that.

Even though you might have already seen this warning, I'd like to yet again remind you that it absolutely isn't a 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 those principles), then you should not resort to stubbing static methods. PowerMock can come in handy 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's static methods twice to get a tax factor for the given person and then calculates a mean value for those two results as follows:

public class MeanTaxFactorCalculator {

    public double calculateMeanTaxFactorFor(Person person) {
      TaxFactorFetcher taxFactorFetcher = new TaxFactorFetcher();
        double taxFactor = taxFactorFetcher.getTaxFactorFor(person);
        double anotherTaxFactor = taxFactorFetcher.getTaxFactorFor(person);
        return (taxFactor + anotherTaxFactor) / 2;
    }

}

Let's assume that TaxFactorFetcher is a class that calculates a person's tax factor in a different way depending on his or her origin.

How to do it...

To use PowerMock with JUnit to stub object instantiation, 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(MeanTaxFactorCalculator.class) since that class needs to be manipulated in order to stub the execution of the TaxFactorFetcher constructor).
  3. Stub object initialization by calling the PowerMockito.whenNew(ClassToStub.class) method together with additional stubbing configuration (whether the constructor has no arguments or has precisely provided parameters, and so on).
  4. 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(MeanTaxFactorCalculator.class)
public class MeanTaxFactorCalculatorTest {
  
    @Mock TaxFactorFetcher taxFactorFetcher;
  
  MeanTaxFactorCalculator systemUnderTest = new MeanTaxFactorCalculator();
  
    @Test
    public void should_calculate_tax_factor_for_a_player_from_undefined_country() throws Exception {
        // given
      double expectedMeanTaxFactor = 10;
      whenNew(TaxFactorFetcher.class).withNoArguments().thenReturn(taxFactorFetcher);
        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) in the @PrepareForTest annotation (in the case of our scenario, this would be @PrepareForTest(MeanTaxFactorCalculator.class) since that class needs to be manipulated in order to stub the execution of the TaxFactorFetcher constructor).
  4. Stub object initialization by calling the PowerMockito.whenNew(ClassToStub.class) method together with the additional stubbing configuration (whether the constructor has no arguments or has precisely provided parameters, and so on).
  5. 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 discussed earlier in terms of the BDDMockito and BDDAssertions usage):

@PrepareForTest(MeanTaxFactorCalculator.class)
public class MeanTaxFactorCalculatorTestNgTest extends PowerMockTestCase {
  
  @Mock TaxFactorFetcher taxFactorFetcher;
  
  MeanTaxFactorCalculator systemUnderTest = new MeanTaxFactorCalculator();
  
  @Test
  public void should_calculate_tax_factor_for_a_player_from_undefined_country() throws Exception {    
    // given
    double expectedMeanTaxFactor = 10;
    whenNew(TaxFactorFetcher.class).withNoArguments().thenReturn(taxFactorFetcher);
    given(taxFactorFetcher.getTaxFactorFor(any(Person.class))).willReturn(5.5, 14.5);

    // when
    double meanTaxFactor = systemUnderTest.calculateMeanTaxFactorFor(new Person());

    // then
    then(meanTaxFactor).isEqualTo(expectedMeanTaxFactor);
  }

    @ObjectFactory
    public IObjectFactory getObjectFactory() {
        return new PowerMockObjectFactory();
    }
  
}

Note

Please remember that you should resort to stubbing object instantiation only if you absolutely know what you are doing: you are familiar with the SOLID principles and you are going to follow them. There are cases (dealing with the legacy code or third-party dependencies) that you would like to mock where using PowerMock can be handy.

Use PowerMock to write the tests for the bad code and then refactor it so that you no longer need to have PowerMock on the classpath.

How it works...

The internals of PowerMock go far beyond the scope of this recipe, but 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.143.0.85