Stubbing methods so that they return custom answers

In this recipe, we will stub a method that returns a value so that it returns a custom answer of our choice.

Getting ready

This recipe is the last that will reuse the example from the previous recipe, which is related to a class that calculates an average value of tax factors. The starting point is the AverageTaxFactorCalculator class and its collaborator is TaxFactorFetcher, which is the provider of those values. The latter class picks one of the tax factors from the database (we'll stub that method). We will test those two classes as a unit. For your convenience, even though it violates the don't repeat yourself (DRY) principle, we will see the classes as follows so that you don't have to scroll around the book too much:

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

}

You can find the TaxFactorFetcher collaborator class in the following code:

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

We'll stub the method that accesses the database in such a way that we will register a callback (an answer) that will check if the person has provided information about his country of origin. Based on that piece of data, we will return a specific value. To do this, you have to perform the following steps:

  1. For the BDD approach, call BDDMockito.willAnswer(answer).given(spy).methodToStub(). Or, in the standard manner, call Mockito.doAnswer(answer).when(spy).methodToStub().
  2. Whichever approach you've chosen, you have to provide the answer to be executed in willAnswer(...) or doAnswer(...), and pass the spy itself in the given(...) or when(...) method.
  3. Remember that the exception that was passed last during stubbing will be thrown for each stubbed method call. Have a look at the following line of code:
    willAnswer(answer1, answer2).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, on how to work with AssertJ or how to do the same with Hamcrest's assertThat(...)). Have a look at the following code:

@RunWith(MockitoJUnitRunner.class)
public class AverageTaxFactorCalculatorTest {

  @Spy TaxFactorFetcher taxFactorFetcher;

  @InjectMocks AverageTaxFactorCalculator systemUnderTest;

    @Test
public void should_return_incremented_tax_factor_while_trying_to_calculate_mean_tax_factor_for_a_person_from_undefined_country() {
        // given
          final double expectedTaxFactor = 107;
        willAnswer(withTaxFactorDependingOnPersonOrigin()).given(taxFactorFetcher).getTaxFactorFromDb(any(Person.class));

        // when
        double avgTaxFactor = systemUnderTest.calculateAvgTaxFactorFor(new Person());

        // then
        then(avgTaxFactor).isEqualTo(expectedTaxFactor);
    }

  private Answer<Object> withTaxFactorDependingOnPersonOrigin() {
    return new Answer<Object>() {      
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {          
          double baseTaxFactor = 50;
          double incrementedTaxFactor = 200;
            if (invocation.getArguments().length > 0) {
                Person person = (Person) invocation.getArguments()[0];
                if (!person.isCountryDefined()) {
                    return incrementedTaxFactor;
                }
            }
            return baseTaxFactor;
        }
    };
  }

}

How it works...

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

There's more...

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

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

willAnswer(answer1).willAnswer(answer2).given(spy).methodToStub()

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

willAnswer(answer1, answer2).given(spy).methodToStub()

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