preface

Mockito is currently the most popular unit testing Mock framework. Using the Mock framework, we can create an external dependency, reduce the coupling between test components, and focus only on the flow and results of the code, so as to truly achieve the test purpose.

The body of the

What is the Mock

Mock, Mock, Mock, Mock For a testing framework, this means constructing a mock/fake object that allows our testing to proceed smoothly.

The Mock test is for objects that are not easy to construct (such as HttpServletRequest must be constructed in the Servlet container) or are not easy to obtain (such as JDBC ResultSet objects). A Mock object is created to test the method.

Why Mock tests

Unit testing is to verify that our code is running correctly. We focus on the flow of the code and whether the results are correct.

If we follow the rules of real code to build external dependencies, it will greatly increase the work of unit testing. The code will also involve too many non-test parts, and the test cases will be complicated and difficult to understand.

Using the Mock framework, we can create an external dependency that only focuses on the flow and results of the code, and actually achieve the test purpose.

Benefits of the Mock testing framework

  1. It can be as simple as virtualizing a complex object (such as virtualizing an interface implementation class).
  2. You can configure themockThe behavior of an object;
  3. Test cases can only focus on the test process and results;
  4. Reduce the coupling of external classes, systems, and dependencies to unit tests.

Mockito process

As shown in the figure, the general process of using Mockito is as follows:

  1. Create an externally dependent Mock object and inject it into the test class.

  2. Execute test code;

  3. Verify that the test code executes correctly.

The use of Mockito

Add the following to the Module’s build.gradle:

dependencies {
    //Mockito for unit tests
    testImplementation "org.mockito:mockito-core:2.+"
    //Mockito for Android tests
    androidTestImplementation 'org.mockito:mockito-android:2.+'
}
Copy the code

Here’s a little explanation:

  • mockito-core: used toLocal unit tests, whose test code path ismodule-name/src/test/java/
  • mockito-android: used toEquipment testing, that is, it needs to runandroidThe device is tested, and its test code path is locatedmodule-name/src/androidTest/java/

The latest version of Mockito-core can be queried in Maven: Mockito-core. The latest version of Mockito-Android can be queried in Maven: Mockito-Android

Examples of Mockito

Normal unit tests use mockito (mockito-core), path: moder-name/SRC /test/ Java /

Here is the official website Demo:

Verify that the behavior associated with the call object is invoked

import static org.mockito.Mockito.*;

// Mock creation
List mockedList = mock(List.class);

// Use mock object - it does not throw any "unexpected interaction" exception
mockedList.add("one"); // The add("one") behavior is called
mockedList.clear(); // The clear() behavior is called

// Selective, explicit, highly readable verification
verify(mockedList).add("one"); // Verify that add("one") has been called
verify(mockedList).clear(); // Verify that clear() has been called
Copy the code

Mock () mock () mock () Mock () Mock () Mock () Mock () Mock () Mock () Mock () Mock () Mock () Mock () Mock Here mockedList performs add(“one”) and clear() behaviors before verify, so verify() will pass.

Configuration/method behavior

// you can mock concrete classes, not only interfaces
LinkedList mockedList = mock(LinkedList.class);
// stubbing appears before the actual execution
when(mockedList.get(0)).thenReturn("first");
// the following prints "first"
System.out.println(mockedList.get(0));
// the following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));
Copy the code

Here are some important points:

when(mockedList.get(0)).thenReturn(“first”)

Mockito Mockito Mockito Mockito When mockedList calls get() with an argument of 0, it returns “first”, which is the result of customizing our mock object’s behavior (mock LinkedList is mockedList, specifying its behavior get(0), Returns “first”).

mockedList.get(999)

Because mockedList does not specify the behavior of get(999), the result is null. Because the underlying principle of Mockito is to use CGlib to dynamically generate a proxy class object, the mock object is essentially a proxy that returns null by default without configured/specified behavior.

The above Demo uses the static method mock() to simulate an instance. We can also simulate an instance with the @mock annotation:

@Mock
private Intent mIntent;

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();

@Test
public void mockAndroid(a){
    Intent intent = mockIntent();
    assertThat(intent.getAction()).isEqualTo("com.yn.test.mockito");
    assertThat(intent.getStringExtra("Name")).isEqualTo("Whyn");
}

private Intent mockIntent(a){
    when(mIntent.getAction()).thenReturn("com.yn.test.mockito");
    when(mIntent.getStringExtra("Name")).thenReturn("Whyn");
    return mIntent;
}
Copy the code

There are two ways to initialize a member variable with @mock, @Spy, @injectMocks and so on:

  1. Add @runWith (MockitoJUnitRunner.class) toJUnit test class

  2. Within the @ Before method are marked call initialization method: MockitoAnnotations. InitMocks (Object)

In the above test case, there is one more way to initialize member variables for @mock annotations, such as MockitoRule. Rules MockitoRule automatically help us call MockitoAnnotations. InitMocks (this) to instantiate the annotation member variables, we need not initialized manually.

Mockito’s important method

Instantiate the virtual object

// You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);

// Stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());

// Following prints "first"
System.out.println(mockedList.get(0));
// Following throws runtime exception
System.out.println(mockedList.get(1));
// Following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));

// Although it is possible to verify a stubbed invocation, usually it's just redundant
// If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
// If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here.
verify(mockedList).get(0);
Copy the code
  • For all methods, the mock object returns null by default, the primitive type/primitive wrapper class default, or an empty collection. For example, return 0 for int/Integer and false for Boolean /Boolean.

  • The behavior configuration (stub) can be duplicated: for example, the normal behavior of an object has a configuration, but the test method can replicate the behavior. Keep in mind that behavioral duplicates may indicate too many underlying behaviors.

  • Once the behavior is configured, the method always returns the configuration value, no matter how many times the method is called.

  • The last behavior configuration is even more important when you configure the same method many times with the same parameters, and the last one takes effect.

The parameter match

Mockito verifies that the parameters are consistent using the equals() method of the parameter object. When more flexibility is required, you can use the parameter matcher:

// Stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
// Stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
when(mockedList.contains(argThat(isValid()))).thenReturn("element");
// Following prints "element"
System.out.println(mockedList.get(999));
// You can also verify using an argument matcher
verify(mockedList).get(anyInt());
// Argument matchers can also be written as Java 8 Lambdas
verify(mockedList).add(argThat(someString -> someString.length() > 5));
Copy the code

Parameter matchers allow for more flexible validation and behavior configuration. See ArgumentMatchers, MockitoHamcrest for more examples of built-in matchers and custom ArgumentMatchers

Note: If a parameter matcher is used, then all parameters need to be supplied with a parameter matcher.

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
// Above is correct - eq() is also an argument matcher
verify(mock).someMethod(anyInt(), anyString(), "third argument");
// Above is incorrect - exception will be thrown because third argument is given without an argument matcher.
Copy the code

Matchers such as anyObject() and eq() do not return matching values. They internally record a matcher stack and return a null value (usually null). This implementation is designed to match the Static type safety of the Java compiler. The consequence of this is that you cannot use anyObject(), eq(), etc., outside of the validation/configuration methods.

Check the number

LinkedList mockedList = mock(LinkedList.class);
// Use mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");

// Follow two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");

// Exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");

// Verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");

// Verification using atLeast()/atMost()
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times");
Copy the code

Check times methods are commonly used as follows:

Method Meaning
times(n) Times n, default is 1 (times(1))
never() Times 0, which is the same thing as times 0.
atLeast(n) At least n times
atLeastOnce() At least one
atMost(n) Up to n times

An exception is thrown

doThrow(new RuntimeException()).when(mockedList).clear();
// following throws RuntimeException
mockedList.clear();
Copy the code

Check in order

Sometimes there is a sequence of actions, so when we are checking, we need to consider the sequence of actions:

// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
// Use a single mock
singleMock.add("was added first");
singleMock.add("was added second");
// Create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock);
// Following will make sure that add is first called with "was added first, then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");

// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
// Use mocks
firstMock.add("was called first");
secondMock.add("was called second");
// Create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);
// Following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
Copy the code

The stub is called consecutively

For the same method, if we want it to return different values in multiple calls, we can use the stub to call it consecutie:

when(mock.someMethod("some arg"))
    .thenThrow(new RuntimeException())
    .thenReturn("foo");

// First call: throws runtime exception:
mock.someMethod("some arg");
// Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));
// Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));
Copy the code

You can also call methods consecutively using the more concise stub below:

when(mock.someMethod("some arg")).thenReturn("one"."two"."three");
Copy the code

Note: Continuous stub calls require chained calls. If multiple stub configurations of the same method are used, only the last one will work (overriding the previous stub configuration).

// All mock.someMethod("some arg") calls will return "two"
when(mock.someMethod("some arg").thenReturn("one")
when(mock.someMethod("some arg").thenReturn("two")
Copy the code

No return value function

For methods with a return type of void, the stub requires a different form of the when(Object) function, because the compiler requires that no void methods exist in parentheses.

For example, stubbing a method of return type void requires that an exception be thrown when called:

doThrow(new RuntimeException()).when(mockedList).clear();
// Following throws RuntimeException:
mockedList.clear();
Copy the code

Monitoring real objects

All I’ve used before is to mock out an object. Thus, the result will return an empty type if no specific behavior is configured for the/stub. If you use a Spy, it calls methods on the original object for behavior without a stub. Think of a Spy as a local mock.

List list = new LinkedList();
List spy = spy(list);

// Optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);
// Use the spy calls *real* methods
spy.add("one");
spy.add("two");

// Prints "one" - the first element of a list
System.out.println(spy.get(0));
// Size() method was stubbed - 100 is printed
System.out.println(spy.size());
// Optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");
Copy the code

Note: Because spy is a local mock, sometimes stubbing is not possible when using when(Object). At this point, can consider to use the doReturn () | Answer () | Throw the stub () this kind of method:

List list = new LinkedList();
List spy = spy(list);
// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");
// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
Copy the code

Spy is not a proxy for real objects. Instead, it clones the real object passed in. Therefore, any action on the real object is not perceived by the Spy object. Similarly, any action on the Spy object does not affect the real object.

Of course, if you use a mock mock object part and through doCallRealMethod () | thenCallRealMethod () method may be used:

// You can enable partial mock capabilities selectively on mocks:
Foo mock = mock(Foo.class);
// Be sure the real implementation is 'safe'.
// If real implementation throws exceptions or depends on specific state of the object then you're in trouble.
when(mock.someMethod()).thenCallRealMethod();
Copy the code

Test-driven development

Using the //given //when //then comments in a behavior-driven development format to write test cases for test usage cornerstones is the official Mockito method for writing test cases and is strongly recommended.

 import static org.mockito.BDDMockito.*;

 Seller seller = mock(Seller.class);
 Shop shop = new Shop(seller);

 public void shouldBuyBread(a) throws Exception {
     // Given
     given(seller.askForBread()).willReturn(new Bread());
     // When
     Goods goods = shop.buyBread();
     // Then
     assertThat(goods, containBread());
 }
Copy the code

Custom error check output information

// Will print a custom message on verification failure
verify(mock, description("This will print on failure")).someMethod();
// Will work with any verification mode
verify(mock, times(2).description("someMethod should be called twice")).someMethod();
Copy the code

@InjectMock

When using the @InjectMock annotation, Mockito checks the class constructor, method, or member variable and automatically mocks it based on their type.

public class InjectMockTest {
    @Mock
    private User user;
    @Mock
    private ArticleDatabase database;
    @InjectMocks
    private ArticleManager manager;
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Test
    public void testInjectMock(a) {
        // Calls addListener with an instance of ArticleListener
        manager.initialize();
        // Validate that addListener was called
        verify(database).addListener(any(ArticleListener.class));
    }

    public static class ArticleManager {
        private User user;
        private ArticleDatabase database;

        public ArticleManager(User user, ArticleDatabase database) {
            super(a);this.user = user;
            this.database = database;
        }

        public void initialize(a) {
            database.addListener(newArticleListener()); }}public static class User {}public static class ArticleListener {}public static class ArticleDatabase {
        public void addListener(ArticleListener listener) {}}}Copy the code

The member variable Manager is of type ArticleManager, and its upper identifier is @injectMocks. This means that to mock out The ArticleManager, Mockito needs to automatically mock out the ArticleManager construction parameters (i.e. User and Database), and finally mock an ArticleManager to assign to the Manager.

Parameters to capture

ArgumentCaptor allows the contents of method arguments to be retrieved during verify, which allows the calling method arguments to be captured and tested during testing.

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Captor
private ArgumentCaptor<List<String>> captor;
@Test
public void testArgumentCaptor(a){
    List<String> asList = Arrays.asList("someElement_test"."someElement");
    final List<String> mockedList = mock(List.class);
    mockedList.addAll(asList);

    verify(mockedList).addAll(captor.capture()); // When verify,you can capture the arguments of the calling method
    final List<String> capturedArgument = captor.getValue();
    assertThat(capturedArgument, hasItem("someElement"));
}
Copy the code

The limitations of Mocktio

  1. Can’tmockStatic method;
  2. Can’tmockThe constructor;
  3. Can’tmock equals()hashCode()Methods.

Welcome to pay attention to the technical public number: one technology Stack

This account will continue to share backend technology essentials, including virtual machine fundamentals, multi-threaded programming, high-performance frameworks, asynchronous, cache and messaging middleware, distributed and microservices, architecture learning and advanced learning materials and articles.