Mockito: Part 2: Creating a stub

Zero: Prerequisite

Let’s start with one word: stub (or piling), which refers to an operation that specifies a return strategy for a method (there are two types: 1 specifies the return value, 2 specifies the actual code logic to be executed when the method is called using doCallRealMethod() or thenCallRealMethod()), The function is to either directly return the value we specified when the test executes to the method (without executing the actual code logic of the method) or execute the actual code logic of the method and return. Such as:

Let’s define an object:

public class Mock {

    public String m1(a) {
        throw newRuntimeException(); }}Copy the code

Then we do the following:

Mock m = Mockito.mock(Mock.class);

// Stub the m1() method
Mockito.doReturn("1").when(m).m1();
Copy the code

Based on the above code we can say: we stubbed the m1() method of the m object.

When we call m1() on the m object, we can get the return value “1” without executing the actual code logic of m1:

Assert.assertEquals("1",m.m1());
Copy the code

The assertion is true at this point.

How to create method stubs:

  • Mockito.when(foo.sum()).thenXXX(…) ;

    • The stub for the foo.sum() method.
    • There is a problem:
      • The Foo object should be a mock object. Stubbing spy objects in this way is not recommended. Because when(foo.sum()). The foo.sum() method is executed first. The actual code logic that causes the sum() method to be executed. Whether the actual code logic for (sum() is executed depends on the type of the spy object, not when the spy object is a mock object or interface – these types also have no actual code logic to execute. The actual code logic is executed when a specific object is spy)
  • Mockito.doXXX(…) .when(foo).sum();

    • The stub for the foo.sum() method.
    • You can stub the void method.
    • The Foo object can be a mock object or a Spy object.
    • This method is recommended for method stubs. It avoids the problem of the thenXXX() approach above.
  • Mockito.doXXX(….) .when(foo.sum());

    • You get an exception that should not be used this way!

    • org.mockito.exceptions.misusing.UnfinishedStubbingException: 
      Unfinished stubbing detected here:
      -> at c.FooTest.verifyTest(FooTest.java:23)
      
      E.g. thenReturn() may be missing.
      Examples of correct stubbing:
          when(mock.isOk()).thenReturn(true);
          when(mock.isOk()).thenThrow(exception);
          doThrow(exception).when(mock).someVoidMethod();
      Hints:
       1. missing thenReturn(a)
       2. you are trying to stub a final method, which is not supported
       3. you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed
      Copy the code

Two: define the return value of the way:

  • Then_xxx method Do_XXX method function
    then(Answer<? > answer) doAnswer(Answer answer) Return value Uses the custom Answer policy.
    thenAnswer(Answer<? > answer) Same as above Same as above.
    thenReturn(T value) doReturn(Object toBeReturned) Specify the return value directly.
    thenReturn(T value, T… values) doReturn(Object toBeReturned, Object… toBeReturnedNext) Specify the return value directly. You can define multiple return values. The first call to the stub method returns the first return value. And so on. Calls that exceed the number of return values return the last return value of the argument.
    thenCallRealMethod() doCallRealMethod() Call the actual code logic. No return value is specified.
    thenThrow(Throwable… throwables) doThrow(Throwable… toBeThrown) Throws an exception when the stub method is called.
    Same as above doThrow(Class<? extends Throwable> toBeThrown) Throws an exception when the stub method is called. Multiple exceptions can be defined. The first call to the stub method returns the first exception. And so on. Calls that exceed the number of exceptions return the last exception of the argument.
    Same as above doThrow(Class<? extends Throwable> toBeThrown, Class<? extends Throwable>… toBeThrownNext) Same as above.
    No corresponding method doNothing() The stub mode used by the void method.

Examples of two ways:

public class Bar {

    public int add(int a, int b) {
        return a + b;
    }

    public void badCode(a) {
        throw new RuntimeException("bad bar code"); }}public class Foo {

    private Bar bar;

    public int sum(int a, int b) {
        return bar.add(a, b);
    }

    public int count(a) {
        bar.badCode();
        return 5; }}Copy the code
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

    // Member variables inside foo are automatically injected into objects generated by the @mock annotation.
    @InjectMocks
    private Foo foo;

    // The bar object is automatically injected into the @injectMocks object's member variables.
    @Mock
    private Bar bar;

    @Test
    public void thenTest(a) {
        Mockito.when(bar.add(1.2)).then(new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                return 7; }});int result = foo.sum(1.2);

        Assert.assertEquals(7, result);
    }

    @Test
    public void thenAnswerTest(a) {
        Mockito.when(bar.add(1.2)).thenAnswer(new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                return 7; }});int result = foo.sum(1.2);

        Assert.assertEquals(7, result);
    }

    @Test
    public void thenReturnTest(a) {
        Mockito.when(bar.add(1.2)).thenReturn(7);

        int result = foo.sum(1.2);

        Assert.assertEquals(7, result);

        result = foo.sum(1.2);
        Assert.assertEquals(7, result);

        result = foo.sum(1.2);
        Assert.assertEquals(7, result);
    }

    @Test
    public void thenReturn2Test(a) {
        Mockito.when(bar.add(1.2)).thenReturn(7.8.9);

        int result = foo.sum(1.2);

        Assert.assertEquals(7, result);

        result = foo.sum(1.2);
        Assert.assertEquals(8, result);

        result = foo.sum(1.2);
        Assert.assertEquals(9, result);

        result = foo.sum(1.2);
        Assert.assertEquals(9, result);

        result = foo.sum(1.2);
        Assert.assertEquals(9, result);
    }

    @Test
    public void thenCallRealMethodTest(a) {
        Mockito.when(bar.add(1.2)).thenCallRealMethod();

        int result = foo.sum(1.2);

        Assert.assertEquals(3, result);
    }

    @Test
    public void thenThrowTest(a) {
        Mockito.when(bar.add(1.2)).thenThrow(new IllegalArgumentException("xxx"));

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            if (e instanceof IllegalArgumentException) {
                RuntimeException re = (RuntimeException)e;
                Assert.assertEquals("xxx", re.getMessage());
                return;
            }
        }

        Assert.fail();
    }

    @Test
    public void thenThrow2Test(a) {
        Mockito.when(bar.add(1.2))
            .thenThrow(new IllegalArgumentException("xxx"), new IllegalArgumentException("yyy"));

        Exception e1 = null;

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("xxx", e1.getMessage());
            e1 = null;
        } else {
            Assert.fail();
        }

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("yyy", e1.getMessage());
            e1 = null;
        } else {
            Assert.fail();
        }

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("yyy", e1.getMessage());
            return;
        } else{ Assert.fail(); } Assert.fail(); }}Copy the code
package cn.theten52.demo.maven;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

    // Member variables inside foo are automatically injected into objects generated by the @mock annotation.
    @InjectMocks
    private Foo foo;

    // The bar object is automatically injected into the @injectMocks object's member variables.
    @Mock
    private Bar bar;

    @Test
    public void doAnswerTest(a) {
        Answer<Integer> answer = new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                return 7; }}; Mockito.doAnswer(answer).when(bar).add(1.2);

        int result = foo.sum(1.2);

        Assert.assertEquals(7, result);
    }

    @Test
    public void doReturnTest(a) {
        Mockito.doReturn(7).when(bar).add(1.2);

        int result = foo.sum(1.2);

        Assert.assertEquals(7, result);

        result = foo.sum(1.2);
        Assert.assertEquals(7, result);

        result = foo.sum(1.2);
        Assert.assertEquals(7, result);
    }

    @Test
    public void doReturn2Test(a) {
        Mockito.doReturn(7.8.9).when(bar).add(1.2);

        int result = foo.sum(1.2);

        Assert.assertEquals(7, result);

        result = foo.sum(1.2);
        Assert.assertEquals(8, result);

        result = foo.sum(1.2);
        Assert.assertEquals(9, result);

        result = foo.sum(1.2);
        Assert.assertEquals(9, result);

        result = foo.sum(1.2);
        Assert.assertEquals(9, result);
    }

    @Test
    public void doCallRealMethodTest(a) {
        Mockito.doCallRealMethod().when(bar).add(1.2);

        int result = foo.sum(1.2);

        Assert.assertEquals(3, result);
    }

    @Test
    public void doThrowTest(a) {
        Mockito.doThrow(NullPointerException.class, IllegalArgumentException.class).when(bar).add(1.2);

        Exception e1 = null;

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if(! (e1instanceof NullPointerException)) {
            Assert.fail();
        }

        e1 = null;

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if(! (e1instanceof IllegalArgumentException)) {
            Assert.fail();
        }

        e1 = null;

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if(! (e1instanceof IllegalArgumentException)) {
            Assert.fail();
        } else {
            return;
        }

        Assert.fail();
    }

    @Test
    public void doThrow2Test(a) {
        Mockito
            .doThrow(new IllegalArgumentException("xxx"), new IllegalArgumentException("yyy"))
            .when(bar).add(1.2);

        Exception e1 = null;

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("xxx", e1.getMessage());
            e1 = null;
        } else {
            Assert.fail();
        }

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("yyy", e1.getMessage());
            e1 = null;
        } else {
            Assert.fail();
        }

        try {
            foo.sum(1.2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("yyy", e1.getMessage());
            return;
        } else {
            Assert.fail();
        }

        Assert.fail();
    }

    @Test
    public void doNotingTest(a) {
        Mockito.doNothing().when(bar).badCode();

        int count = foo.count();
        Assert.assertEquals(5, count); }}Copy the code

Matters needing attention:

  1. By default, the mock returns NULL, raw/original wrapper value, or an empty set for all method returns, depending on the case. For example, int/Integer returns 0 and Boolean/Boolean returns false.

  2. Stubs can be overwritten: For example, a normal stub can be set when entering a test fixture (before method), but can be overwritten in a formal test method. Note that overwriting stubs is a potential code odor that indicates too many stubs.

    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.Mockito;
    import org.mockito.junit.MockitoJUnitRunner;
    
    @RunWith(MockitoJUnitRunner.class)
    public class MockitoTest {
    
        // Member variables inside foo are automatically injected into objects generated by the @mock annotation.
        @InjectMocks
        private Foo foo;
    
        // The bar object is automatically injected into the @injectMocks object's member variables.
        @Mock(lenient = true)
        private Bar bar;
    
        @Before
        public void before(a) {
        		// This stub is overwritten
            Mockito.doReturn(6).when(bar).add(1.2);
        }
    
        @Test
        public void doReturnTest(a) {
            Mockito.doReturn(7).when(bar).add(1.2);
    
            int result = foo.sum(1.2);
    
            Assert.assertEquals(7, result); }}Copy the code

    Or:

     // All mock. SomeMethod ("some arg") returns "two"
     Mockito.when(mock.someMethod("some arg")).thenReturn("one")
     Mockito.when(mock.someMethod("some arg")).thenReturn("two")
    Copy the code

    When the mock checks for unnecessary stubs (defined but not used), it throws an exception:

    org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
    Unnecessary stubbings detected in test class: MockitoTest
    Clean & maintainable test code requires zero unnecessary code.
    Following stubbings are unnecessary (click to navigate to relevant line of code) : 1. - >at cn.xxx.demo.MockitoTest.before(MockitoTest.java: 25)Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
    Copy the code

    This can be resolved by removing unnecessary stub code, or by using @mock (lenient = true) to flag the existence of Mock objects that do not require stubs:

        @Mock(lenient = true)
        private Bar bar;
    Copy the code
  3. Once stubbed, the method will always return a stub value, no matter how many times it is called.

    1. Refer to the above,thenReturnTest(),doReturnTest()Serial methods.
  4. The last stub is more important – when you stub the same method with the same argument multiple times. Calls that exceed the stub count end up with the stub’s return policy. In other words: the order of stubs is ** important, ** but it is only rarely meaningful, such as when stubs are exactly the same method calls or when parameter matchers are used.

    1. Refer to the above,thenReturn2Test(),thenThrowTest(),thenThrow2Test(),doThrowTest(),doThrow2Test(),doReturn2Test()Serial methods.
  5. We can stub not only mock objects (typically objects annotated by @mock), but also spy objects (typically objects annotated by @Spy).

Three: Customize the return policy

About the return strategy. Please refer to this article in the Java Testing Framework Series: Mockito: Part 1: Object Creation.

Change the default return value of an unstubbed method.

We know that by default, a mock will return NULL, raw/raw wrapper value, or an empty set for all method return values, depending on the case.

So can we change the return value of a specific method if we stub it? Sure, please refer to this series of articles for details.

Five: parameter matcher

1: introduction

The parameter matcher can be used in method stubs when we are not sure what the exact value of the method to be stubbed will be when it is called. (It can also be used for validation/assertion at the end of the test, which we’ll cover in the next article)

Mockito parameters validate values using the natural Java style: that is, by using the equals() method. Sometimes, when you need extra flexibility, you can use the ArgumentMatcher (ArgumentMatcher or ArgumentCaptor) :

 // Use the built-in anyInt() argument matcher to create stubs
 when(mockedList.get(anyInt())).thenReturn("element");

 // Use a custom parameter matcher isValid()
 when(mockedList.contains(argThat(isValid()))).thenReturn(true);

 / / output "element"
 System.out.println(mockedList.get(999));

 // You can also use the parameter matcher for validation
 verify(mockedList).get(anyInt());

 // Parameter matchers can also be written in the Java 8 Lambdas style
 verify(mockedList).add(argThat(someString -> someString.length() > 5));
Copy the code

Mockito extends ArgumentMatchers to access all matchers simply by statically importing the Mockito class.

 // stub with anyInt() matcher
 when(mockedList.get(anyInt())).thenReturn("element");

 // The following code prints "element"
 System.out.println(mockedList.get(999));

 // You can also use parameter matchers when validating
 verify(mockedList).get(anyInt());
 
Copy the code

Because Mockito performs type checking on the matchers of the any(Class) and anyInt families, they do not match null arguments. Instead, use the isNull matcher.

 // Use the anyBoolean() argument matcher for stubs
 when(mock.dryRun(anyBoolean())).thenReturn("state");

 // The following stubs will not match and will not return "state"
 mock.dryRun(null);

 // You can change the stub to this:
 when(mock.dryRun(isNull())).thenReturn("state");
 mock.dryRun(null); // ok

 // Change the code:
 when(mock.dryRun(anyBoolean())).thenReturn("state");
 mock.dryRun(true); // ok

 
Copy the code

The same applies to validation using a parameter matcher.

Tip:

When we encounter arguments that might be null, we can lazily use any() to match. It can match null and non-NULL values.

Warning: If you use a parameter matcher, all parameters must be supplied by the matcher. For example: (The example shows validation operations, but also applies to method stubs) :

 verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
 // The above code is suitable for the -eq () method when the argument matcher

 verify(mock).someMethod(anyInt(), anyString(), "third argument");
 // The above code is not correct - an exception will be thrown because the third argument is not given by the argument matcher.
 
Copy the code

Matcher methods such as anyObject() and eq() do not return matchers. Internally, they record a matcher on the stack and return a dummy value (usually empty). This implementation is due to static type safety imposed by the Java compiler. The result is that you cannot use anyObject(),eq() methods outside of validation/stubs.

Parameter matchers allow flexible validation or stubs. See more examples of built-in matchers and ** custom parameter matchers/Hamcrest matchers **. (Also described below)

Use complex parameter matching wisely. The natural matching style of occasionally using equals() with anyX() matchers tends to provide clean and simple tests. Sometimes it’s best to refactor your code to allow equals() to match or even implement the equals() method to help with testing.

Also, read Section 15 or Javadoc for ArgumentCaptor classes. ArgumentCaptor is a special implementation of an argument matcher that captures argument values for further assertions.

Warning about parameter matchers:

If you use a parameter matcher, all parameters must be supplied by the matcher.

The following example shows the use of a parameter matcher for validation, but it also works for stub method calls:

   verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
   Eq () is also a parameter matcher

   verify(mock).someMethod(anyInt(), anyString(), "third argument");
   // The above is written incorrectly - the exception is thrown because the third argument is not in the form of an argument matcher
Copy the code

Matcher methods such as anyObject() and eq() do not return matchers. Internally, they record a matcher on the stack and return a dummy value (usually empty). This implementation is due to static type safety imposed by the Java compiler. The result is that you cannot use anyObject(),eq() methods outside of validation/stubs.

2:@Captor

ArgumentCaptor is allowed to create annotations on the field.

Example:

 public class Test{

    @Captor 
    ArgumentCaptor<AsyncCallback<Foo>> captor;

    private AutoCloseable closeable;

    @Before
    public void open(a) {
       closeable = MockitoAnnotations.openMocks(this);
    }

    @After
    public void release(a) throws Exception {
       closeable.close();
    }

    @Test public void shouldDoSomethingUseful(a) {
       / /...
       verify(mock).doStuff(captor.capture());
       assertEquals("foo", captor.getValue()); }}Copy the code

One of the advantages of using the @captor annotation is that you can avoid the warnings associated with capturing complex generic types.

2: User-defined ArgumentMatcher

Before implementing custom parameter matchers, it is important to understand the use cases and options available for handling non-trivial parameters. This way, you can choose the best method for a given scenario and generate the highest quality (clean and maintainable) tests. Continue reading ArgumentMatcher’s Javadoc to see the method and to see examples. Also check out: AdditionalMatchers.

ArgumentMatcher allows you to create custom argument matchers. This API was changed in Mockito 2.1.0 to decouple Mockito from Hamcrest and reduce the risk of version incompatibilities.

For non-trivial method parameters used in stubs or validation, you have the following options (in no particular order) :

  • Refactor the code so that interactions with collaborators are more easily used for testing. Can different parameters be passed to the method to make the simulation easier? If something is hard to test, it’s usually a sign that the design could be better, so refactor for testability!
  • Don’t match arguments strictly, just use one of any loose ArgumentMatchers, such as argumentmatchers.notnull (). Sometimes it’s better to have a simple test than a complex one that seems to work.
  • Implement equals() on objects used as mock parameters. Mockito naturally uses equals() for parameter matching. A lot of times, this is a clean and easy option.
  • Used in ArgumentCaptor to capture arguments and assert their state. Useful when you need to validate parameters. If you need parameter matching for stubbing, Captor is useless. Many times, this option enables clean and readable tests through fine-grained validation of parameters.
  • By implementing ArgumentMatcher interface and pass to realize ArgumentMatchers. ArgThat (org. Mockito. ArgumentMatcher) ways to use custom parameters matching unit. This option is useful if the stub requires a custom matcher and can be reused multiple times. Please note that ArgumentMatchers. ArgThat (org. Mockito. ArgumentMatcher) exist in the process of automatic unpacking NullPointerException.
  • If you already have a Hamcrest Matcher, it is useful to use an instance of the Hamcrest Matcher and pass it to mockitohamcrea.argthat (org.hamcrea.matcher). Reuse will pay off! ArgThat (org.hamcrea.matcher) has NullPointerException during automatic unpacking.
  • Java 8 only – use lambda instead of ArgumentMatcher. ArgumentMatcher is actually a functional interface. And the lambda ArgumentMatchers. ArgThat (org. Mockito. ArgumentMatcher) methods are used together.

ArgumentMatcher interface implementation can and ArgumentMatchers. ArgThat (org. Mockito. ArgumentMatcher) methods are used together. Use the toString() method to describe the matcher – it is printed in the validation error.

 class ListOfTwoElements implements ArgumentMatcher<List> {
     public boolean matches(List list) {
         return list.size() == 2;
     }
     public String toString(a) {
         // Print validation error
         return "[list of 2 elements]";
     }
 }

 List mock = mock(List.class);

 when(mock.addAll(argThat(new ListOfTwoElements()))).thenReturn(true);

 mock.addAll(Arrays.asList("one"."two"));

 verify(mock).addAll(argThat(new ListOfTwoElements()));
 
Copy the code

To maintain readability, you can extract methods such as:

   verify(mock).addAll(argThat(new ListOfTwoElements()));
   // Replace with:
   verify(mock).addAll(listOfTwoElements());
 
Copy the code

In Java 8, you can treat ArgumentMatcher as a functional interface and use lambda, for example:

   verify(mock).addAll(argThat(list -> list.size() == 2));
Copy the code

Read more about other Matchers in The Javadoc for Matchers.

2.1.0 Migration Guide

All existing custom ArgumentMatcher implementations will no longer compile for long periods. ArgThat (), which passes all locations of the Hamcrest matcher, will no longer compile long. There are two ways to solve the problem:

  • A) Refactor the Hamcrest adapter to the Mockito adapter: use “implements ArgumentMatcher” instead of “extends ArgumentMatcher”. thendescribeTo()Method is reconstructed astoString()Methods.
  • B) useorg.mockito.hamcrest.MockitoHamcrest.argThat()Instead ofMockito.argThat(). Make sure thathamcrestDependencies exist on the classpath (Mockito no longer depends on Hamcrest).

What option suits you? If you don’t mind compiling a hamcrest dependency, option B) may be right for you. Your choice should have little impact and be completely reversible – you can choose different options (and refactor the code) in the future.

3:ArgumentCaptor

Mockito implements the natural Java-style validation of parameter values by using the equals() method. This is also the recommended way to match parameters, because it makes testing clean and easy. In some cases, it may be helpful to assert certain parameters after actual validation. Such as:

   ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
   verify(mock).doSomething(argument.capture());
   assertEquals("John", argument.getValue().getName());
Copy the code

Examples of capturing mutable parameters:

   // Capture variables:
   ArgumentCaptor<Person> varArgs = ArgumentCaptor.forClass(Person.class);
   verify(mock).varArgMethod(varArgs.capture());
   List expected = asList(new Person("John"), new Person("Jane"));
   assertEquals(expected, varArgs.getAllValues());
Copy the code

Warning: It is recommended to use ArgumentCaptor with validation, but not with stubs. Using stub ArgumentCaptor can reduce the readability of the test, because captor is created outside the assertion (aka validation or “then”) block. It also reduces defect location because no parameters are captured if the stub method is not called.

ArgumentCaptor is, in a way, related to custom argument matchers (see Javadoc for the ArgumentMatcher class). Both techniques can be used to ensure that certain parameters are passed to mock objects. However, ArgumentCaptor may be more appropriate in the following cases:

  • Custom parameter matchers are unlikely to be reused
  • You only need it to assert the parameter values to complete the validation

A custom ArgumentMatcher is usually better for stubs.

4: appendix:

AdditionalMatchersMethod introduction:

The AdditionalMatchers class provides very few matchers, although they can be useful in combining multiple matchers or negating necessary matchers.

Modifiers and types Methods and Instructions
static <T> T any()matchingAny content, including null values and mutable parameters.
static <T> T any(Class<T> type)Matches any object of the given type, excluding null values.
static boolean anyBoolean()anybooleanorIs not empty Boolean
static byte anyByte()anybyteorIs not empty Byte.
static char anyChar()anycharorIs not empty Character.
static <T> Collection<T> anyCollection()anyNot null Collection.
static <T> Collection<T> anyCollectionOf(Class<T> clazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static double anyDouble()anydoubleorIs not empty Double.
static float anyFloat()anyfloatorIs not empty Float.
static int anyInt()Any intOr not null Integer.
static <T> Iterable<T> anyIterable()anyNot null 可迭代.
static <T> Iterable<T> anyIterableOf(Class<T> clazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static <T> List<T> anyList()anyNot null List.
static <T> List<T> anyListOf(Class<T> clazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static long anyLong()anylongorIs not empty Long.
static <K,V> Map<K,V> anyMap()anyNot null Map.
static <K,V> Map<K,V> anyMapOf(Class<K> keyClazz, Class<V> valueClazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static <T> T anyObject()Have been abandoned. This will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static <T> Set<T> anySet()anyNot null Set.
static <T> Set<T> anySetOf(Class<T> clazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static short anyShort()anyshortorIs not empty Short.
static String anyString()anyIs not empty String
static <T> T anyVararg()Have been abandoned. Used since 2.1.0any()
static <T> T argThat(ArgumentMatcher<T> matcher)Allows the creation of custom parameter matchers.
static boolean booleanThat(ArgumentMatcher<Boolean> matcher)Allows you to create customizationsbooleanParameter matcher.
static byte byteThat(ArgumentMatcher<Byte> matcher)Allows you to create customizationsbyteParameter matcher.
static char charThat(ArgumentMatcher<Character> matcher)Allows you to create customizationscharParameter matcher.
static String contains(String substring)Containing a givenStringArguments to a substring.
static double doubleThat(ArgumentMatcher<Double> matcher)Allows you to create customizationsdoubleParameter matcher.
static String endsWith(String suffix)For a givenStringParameter ending with suffix.
static boolean eq(boolean value)Is equal to a givenbooleanValue.
static byte eq(byte value)Is equal to a givenbyteValue.
static char eq(char value)Is equal to a givencharValue.
static double eq(double value)Is equal to a givendoubleValue.
static float eq(float value)Is equal to a givenfloatValue.
static int eq(int value)Is equal to a givenintValue.
static long eq(long value)Is equal to a givenlongValue.
static short eq(short value)Is equal to a givenshortValue.
static <T> T eq(T value)An object parameter equal to the given value.
static float floatThat(ArgumentMatcher<Float> matcher)Allows you to create customizationsfloatParameter matcher.
static int intThat(ArgumentMatcher<Integer> matcher)Allows you to create customizationsintParameter matcher.
static <T> T isA(Class<T> type)To achieve a givenObjectClass parameters.
static <T> T isNotNull()notnullThe judgment.
static <T> T isNotNull(Class<T> clazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static <T> T isNull()isnullThe judgment.
static <T> T isNull(Class<T> clazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static long longThat(ArgumentMatcher<Long> matcher)Allows you to create customizationslongParameter matcher.
static String matches(Pattern pattern)And the given regular expressionPatternMatched parameters.
static String matches(String regex)Matches the specified regular expressionStringConfiguration parameters.
static <T> T notNull()notnullThe judgment.
static <T> T notNull(Class<T> clazz)Have been abandoned. In Java 8, this method will be removed in Mockito 4.0. This method is used for general friendliness only to avoid casts and is no longer required in Java 8.
static <T> T nullable(Class<T> clazz)The parameters are eithernullOr of a given type.
static <T> T refEq(T value, String... excludeFields) Reflexivity equalsObject argument with a given value, allowing the selected field to be excluded from the class.
static <T> T same(T value)An object parameter with the same value as the given value.
static short shortThat(ArgumentMatcher<Short> matcher)Allows you to create customizationsshortParameter matcher.
static String startsWith(String prefix)For a givenStringArguments that start with a prefix.

Hamcrest matchers

Hamcrest matcher is allowed to match parameters. Hamcrest dependencies need to be included on the classpath. Mockito does not depend on Hamcrest! Note the NullPointerException warning in the automatic unpacking process described below.

Before implementing or reusing an existing Hamcrest matcher, read how to handle ArgumentMatcher.

Mockito 2.1.0 was separated from Hamcrest to avoid incompatibilities that affected our users in the past. Mockito provides a dedicated API ArgumentMatcher to match arguments. Hamcrest integration is provided so that users can leverage existing Hamcrest matchers.

Example:

     import static org.mockito.hamcrest.MockitoHamcrest.argThat;

     // do the stub
     when(mock.giveMe(argThat(new MyHamcrestMatcher())));

     / / verification
     verify(mock).giveMe(argThat(new MyHamcrestMatcher()));
 
Copy the code

NullPointerException warning during automatic unboxing: In rare cases, when matching primitive parameter types, you must use intThat(), floatThat(), and other related methods. This way you can avoid NullPointerexceptions during automatic unpacking. Because of the way Java works, we don’t really have a clean way to detect this and protect users from this problem. Hopefully this paragraph describes the problem and the solution well. If you know how to fix the problem, please let us know via the mailing list or issue tracker.

Modifiers and types Methods and Instructions
static <T> T argThat(org.hamcrest.Matcher<T> matcher)Hamcrest matcher is allowed to match parameters.
static boolean booleanThat(org.hamcrest.Matcher<Boolean> matcher)Enable and originalbooleanParameter matching integrated Hamcrest matcher.
static byte byteThat(org.hamcrest.Matcher<Byte> matcher)Enable and originalbyteParameter matching integrated Hamcrest matcher.
static char charThat(org.hamcrest.Matcher<Character> matcher)Enable and originalcharParameter matching integrated Hamcrest matcher.
static double doubleThat(org.hamcrest.Matcher<Double> matcher)Enable and originaldoubleParameter matching integrated Hamcrest matcher.
static float floatThat(org.hamcrest.Matcher<Float> matcher)Enable and originalfloatParameter matching integrated Hamcrest matcher.
static int intThat(org.hamcrest.Matcher<Integer> matcher)Enable and originalintParameter matching integrated Hamcrest matcher.
static long longThat(org.hamcrest.Matcher<Long> matcher)Enable and originallongParameter matching integrated Hamcrest matcher.
static short shortThat(org.hamcrest.Matcher<Short> matcher)Enable and originalshortParameter matching integrated Hamcrest matcher.

Six: Best practices

  • 1. Which method should we use, doXXX() or thenXXX()?
    • The doXXX() family of methods is recommended for all stub operations on methods.
    • First: The do family of methods can handle stubs of void correctly.
    • Second: The DO series of methods can work well with stub Spy objects. (Avoid the problem that the actual code logic of the stubbed method will be called once at stubbing time, as described above.)