Stubbing methods so that they throw exceptions

In this recipe, we will stub a method that returns a value so that it throws an exception. Since we want our code to be beautiful, we'll use the catch-exception library to catch and check the exceptions thrown in our system.

Getting ready

Ensure that you have the catch-exception library on your classpath; refer to the Stubbing methods so that they throw exceptions recipe of Chapter 4, Stubbing Behavior of Mocks, for details on how to add catch-exception to your project.

This recipe will reuse the example from the previous recipe. We have a class that calculates an average value of tax factors (AverageTaxFactorCalculator) and TaxFactorFetcher is the provider of those values. One of the values is picked from the database (and we'll stub that method). We will test those two classes as a unit. For your convenience (so that you don't scroll around the book too much), I'm showing you the classes here (don't worry, they're really small):

public class AverageTaxFactorCalculator {

    private final TaxFactorFetcher taxFactorFetcher;

    public AverageTaxFactorCalculator(TaxFactorFetcher taxFactorFetcher) {
        this.taxFactorFetcher = taxFactorFetcher;
    }

    public double calculateAvgTaxFactorFor(Person person) {
        double taxFactor = taxFactorFetcher.getTaxFactorFromDb(person);
        double anotherTaxFactor = taxFactorFetcher.getTaxFactorFor(person);
        return (taxFactor + anotherTaxFactor) / 2;
    }

}

And its collaborator, TaxFactorFetcher, is as follows:

public class TaxFactorFetcher {

    static final double NO_COUNTRY_TAX_FACTOR = 0.3;
    static final double DEFAULT_TAX_FACTOR = 0.5;
    static final double DB_TAX_FACTOR = 0.8;

    public double getTaxFactorFor(Person person) {
        if (person.isCountryDefined()) {
            return DEFAULT_TAX_FACTOR;
        }
        return NO_COUNTRY_TAX_FACTOR;
    }

    public double getTaxFactorFromDb(Person person) {
        // simulation of DB access
        return DB_TAX_FACTOR;
    }
}

How to do it...

To make your spy throw an exception instead of executing the real logic, you have to follow these simple steps:

  1. For the BDD approach, call BDDMockito.willReturn(value).given(spy).methodToStub(). Or, in the standard manner, call Mockito.doReturn(value).when(spy).methodToStub().
  2. Whichever approach you've chosen, you have to provide the desired output in willReturn(...) or thenReturn(...), and pass the spy itself in the given(...) or when(...) method.
  3. Remember that the value that was passed last during stubbing will be returned for each stubbed method call. Have a look at the following line of code:
    willThrow(exception1, exception2).given(taxFetcher).getTax();

    As shown in the preceding line of code, regardless of the number of taxFetcher.getTax() method executions, you will first throw exception1 and then always throw exception2 (until stubbed again).

Note

You have to bear in mind that if you try to stub a method with the BDDMockito.given(...).willReturn(...) call or in the standard manner—you stub a method with the Mockito.when(...).thenReturn(...) call—then you will actually call the spy's method that you want to stub!

Let's check the JUnit test; see Chapter 1, Getting Started with Mockito, for the TestNG configuration (I'm using the 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(...)). The when(...) method comes from the CatchExceptionAssertJ class; you can use CatchExceptionAssertJ.thenThrown(...) without any unnecessary code in between, as follows:

@RunWith(MockitoJUnitRunner.class)
public class AverageTaxFactorCalculatorTest {

  @Spy TaxFactorFetcher taxFactorFetcher;
  
  @InjectMocks AverageTaxFactorCalculator systemUnderTest;

    @Test
    public void should_throw_exception_while_trying_to_calculate_mean_tax_factor() {
        willThrow(new TaxFactorFetchException()).given(taxFactorFetcher).getTaxFactorFor(any(Person.class));
        
      when(systemUnderTest).calculateAvgTaxFactorFor(new Person());
        
        thenThrown(TaxFactorFetchException.class);      
    }

}

How it works...

A spy is a special case of a mock. Refer to the Stubbing methods so that they throw exceptions recipe of Chapter 4, Stubbing Behavior of Mocks, for more information.

There's more...

Mockito allows you to provide a series of possible thrown exceptions to the stubbed method, either by using the fluent interface API or by means of varargs.

If you need to throw a series of exceptions from the stubbed spy's method using the fluent API, you will have to stub the method invocation as follows:

willThrow(ex1).willThrow(ex2).given(spy).methodToStub()

Or, if you want to use varargs, you have do it as follows:

willThrow(ex1, ex2).given(spy).methodToStub()(ex1, exj2).given(spy).methodToStub()

Note

Note that we are not passing an additional expected parameter to the @Test annotation (the expected parameter suggests that if a test ends by throwing an exception of the given type, then the test has ended successfully). In the majority of cases, you would want to control where the exception is thrown from (otherwise, your test could pass when it shouldn't). That is why, either you should use the try-catch approach (if an exception has not been thrown, the test should fail with a given message), the ExpectedException JUnit rule, or the catch-exception library.

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