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.
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; } }
To make your spy throw an exception instead of executing the real logic, you have to follow these simple steps:
BDDMockito.willReturn(value).given(spy).methodToStub()
. Or, in the standard manner, call Mockito.doReturn(value).when(spy).methodToStub()
.willReturn(...)
or thenReturn(...)
, and pass the spy itself in the given(...)
or when(...)
method.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).
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); } }
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.
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 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.
JUnit ExpectedException Rule vs. Catch-Exception
by Tomasz Kaczanowski, at http://www.kaczanowscy.pl/tomek/2013-03/junit-expected-exception-rule-vs-catch-exception3.144.222.185