By Subham Aggarwal | 6/20/2017 | General |Beginners

Mockito - Stubbing Behavior of Mocks Part 1

Mockito - Stubbing Behavior of Mocks Part 1

In this lesson, we will cover:

  • Using argument matchers for stubbing
  • Stubbing methods that return values
  • Stubbing methods so they throw exceptions

 

As explained in the previous lessons, Mockito is all about creating mocks and stubbing their behavior. It's worth taking another look at the differences between mocks and stubs in order to properly distinguish possible actions that can be taken on either of them. Let us revisit the definition once again here, and quote it:

A Stub is an object that has predefined answers to method executions made during the test whereas a Mock is an object that has predefined answers to method executions made during the test and that has recorded expectations of these executions.

 

As far as Mockito is concerned, it does not respect this difference and treats each test double as a Mock itself.

 

It is important to understand that we will not verify interactions. Instead, we will verify whether the system under the test's logic does what it is supposed to do.

Using argument matchers for stubbing

Before we start looking at different ways of stubbing method calls, we need to define the concept of argument matchers. When passing arguments to the mock's methods during the stubbing process, Mockito verifies argument values using the equals() method. In other words, when calling the following code:

User john = new User();
given(taxFactorFetcher.getTaxFactorFor(john).willReturn(10);

 

Mockito will verify whether the user passed as an argument to the getTaxFactorFor(...) method that is equals to our user (in this case, Mr. John). If that is the case, only then will Mockito return 10 as the output of the getTaxFactorFor(...) method.

Available matchers

Let’s take a look at what Matchers are available in Mockito.

  • Examples of argument matchers that start with the any prefix are any(), any(User.class), anyDouble(), anyList() etc.
  • Argument matchers that end with the That suffix are argThat(...), booleanThat(...), doubleThat(...), and so on.
  • The startsWith(...) and endsWith(...) argument matchers are used for string comparison.
  • The eq(...) argument matcher checks for equality.
  • The isNotNull(), isNull(), and notNull() argument matchers provide verification against null values.
  • The refEq(...) argument matcher is used for reflection-equal verification (checks via reflection whether two objects are equal).

 

Putting them to sample snippets, here are example how matchers can be used.

/* match the method for any user and for the city of Warsaw */
given(irsDataFetcher.isIrsApplicable(any(User.class), eq("John"))).willReturn(true);

/* match the method for any user and for the city starting with 'W' */
given(irsDataFetcher.isIrsApplicable(any(User.class), startsWith("J"))).willReturn(true);

/* match the method for any user and for the city ending with 'w' */
given(irsDataFetcher.isIrsApplicable(any(User.class), endsWith("j"))).willReturn(true);

/* match the method for any user and for any city */
given(irsDataFetcher.isIrsApplicable(any(User.class), anyString())).willReturn(true);

/* match the method for a user that equals another user and for any city */
given(irsDataFetcher.isIrsApplicable(refEq(new User()), anyString())).willReturn(true);

/* match the method for the same reference of the user and for any city */
given(irsDataFetcher.isIrsApplicable(same(user), anyString())).willReturn(true);

/* match the method for a user called Lewandowski and for any city (using Hamcrest matcher) */
given(irsDataFetcher.isIrsApplicable(argThat(new ArgumentMatcher<User>() {

           @Override
           public boolean matches(Object argument) {
               return "Lewandowski".equalsIgnoreCase(((User)argument).getName());
           }

       }), anyString())).willReturn(true);

 

Please note that if you are using a matcher for at least one argument, then you have to provide matchers for all of the arguments.

Stubbing methods that return values

Let’s define a System under Test here called ‘MeanTaxFactorCalculator’, which calls TaxFactorFetcher twice to get a tax factor for the given user and then calculates a mean value for those two results as follows:

public class MeanTaxFactorCalculator {

 private final TaxFactorFetcher taxFactorFetcher;

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

 public double calculateMeanTaxFactorFor(User user) {
   double taxFactor = taxFactorFetcher.getTaxFactorFor(user);
   double anotherTaxFactor =

     taxFactorFetcher.getTaxFactorFor(user);
       return (taxFactor + anotherTaxFactor) / 2;
 }
}

 

To stub non-void methods, let’s follow a few simple steps:

  1. Call
    Mockito.when(mock.methodToStub()).thenReturn(value).
  1. Irrespective of the chosen way in the given(...) or when(...) methods, we need to provide the mock's method call, and in the willReturn(...) or thenReturn(...) methods, we need to provide the required output.
  2. Note that the last passed value while stubbing will be for each stubbed method call. In other words, say that you stub the mock as follows:
given(taxFetcher.getTax()).willReturn(50, 100);


Then, regardless of the number of taxFetcher.getTax() method executions, you will first return 50 and then you will always receive 100 (until it's stubbed again).

Here, what Mockito does internally when we stub methods is that it executes two main actions:

mockito stubbing 

When we call the given(...) or when(...) methods, the validation takes place for the following situations:

  • Stubbing is not complete (when we forget to write thenReturn(...) or willReturn(...))
  • Argument matchers are misplaced (we can't use them outside of verification or stubbing)
  • Stubbing is performed on an object that is not a mock
  • Invalid checked exception is being thrown

As for the answer construction phase, it takes place on the execution of the willReturn(...) or thenReturn(...) method calls. Eventually, Mockito constructs the Returns answer with the passed value and then delegates the execution to it.

Stubbing methods so that they throw exceptions

Now, let us stub a method that returns a value so that it throws an exception. This way, we can simulate scenarios in which some connection issues might occur or some business exceptions have been thrown in our application, like a database connection not found.

Gradle dependency

testCompile 'com.googlecode.catch-exception:catch-exception:1.2.0'

Maven dependency

<dependency>
   <groupId>com.googlecode.catch-exception</groupId>
   <artifactId>catch-exception</artifactId>
   <version>1.2.0</version>
   <scope>test</scope>
</dependency>

For this section, our system under test will again be MeanTaxFactorCalculator, which calls TaxFactorFetcher twice and calculates a mean value out of the received tax factor values as follows:

public class MeanTaxFactorCalculator {

   private final TaxFactorFetcher taxFactorFetcher;

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

   public double calculateMeanTaxFactorFor(User user) {
       double taxFactor = taxFactorFetcher.getTaxFactorFor(user);
       double anotherTaxFactor = taxFactorFetcher.getTaxFactorFor(user);
       return (taxFactor + anotherTaxFactor) / 2;
   }

 

To make the mock's non-void method throw an exception, we need to perform the following steps:

  1. Call
    Mockito.when(mock.methodToStub()).thenThrow(exception)
  1. Regardless of the chosen approach in the given(...) or when(...) method, you have to provide the mock's method call, and in the willThrow(...) or thenThrow(...) method, provide the required exception to throw.
  2. Remember that the last passed value during the stubbing will be thrown for each stubbed method call. In other words, you stub the mock as follows:
given(taxFetcher.getTax()).willThrow(new Exception1(),new Exception2());
  1. Then, regardless of the number of taxFetcher.getTax() method executions, first Exception1() will be thrown and then you will always have Exception2() thrown (until it's stubbed again).

 

The when(...) method comes from the CatchExceptionAssertJ class. This way, I can use CatchExceptionAssertJ.thenThrown(...) without any unnecessary code in between, as shown in the following code:

@RunWith(MockitoJUnitRunner.class)
public class MeanTaxFactorCalculatorTest {

   @Mock TaxFactorFetcher taxFactorFetcher;

   @InjectMocks MeanTaxFactorCalculator systemUnderTest;

   @Test
   public void should_throw_exception_when_calculating_mean_tax_factor() {
       given(taxFactorFetcher.getTaxFactorFor(any(User.class))).willThrow(new TaxServiceUnavailableException());

     when(systemUnderTest).calculateMeanTaxFactorFor(new User());
     thenThrown(TaxServiceUnavailableException.class);
   }
}

 

It's important to take a close look at what happens in this test. Assuming that we have all of the mocks set up and injected, let's move to the test's body. Over there, first, we stub the taxFactorFetcher.getTaxFactorFor(...) method execution so that it throws a TaxServiceUnavailableException exception. Then, we use the CatchExceptionAssertJ.when(...) method to allow the catch-exception library to catch the thrown exception (if there is one). Finally, we use the CatchExceptionAssertJ.thenThrown(TaxServiceUnavailableException.class) method to check whether the thrown exception was of a proper type.

By Subham Aggarwal | 6/20/2017 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now