Mock and Mockito’s relationship

When “mock” is used in software development, it is usually understood as a mock object.

Why simulation? When we first learned to program, the objects we wrote were usually independent, didn’t depend on other classes, and didn’t manipulate other classes. But in fact, software is full of dependencies, for example, we will write operation class based on the Service class, and the service class is based on the data access class (DAO), and then form complex dependencies.

The idea of unit testing is that we want to test code without dependencies. This kind of testing lets you test the validity of your code regardless of its dependencies. The idea is that if the code works as designed and the dependencies work well, they should all work well at the same time.

Sometimes the dependencies our code needs may not be developed or even exist, so how do we keep our development going? Mocks can be used to keep development going. The purpose of mocks is to simulate objects that are difficult or complex to construct in an application, thus separating the test from objects outside the test boundary.

You can implement Mock techniques by writing custom Mock objects yourself, but writing custom Mock objects requires additional coding effort and can introduce errors. There are many excellent open source frameworks that implement mock techniques, and Mockito is an excellent one for unit testing. Mockito is open source on Github at github.com/mockito/moc…

In addition to Mockito, there are several similar frameworks, such as:

  • EasyMock: A popular early MocK testing framework. It provides a simulation of the interface, which can be recorded, played back, and checked to complete the general testing process. It can verify the type, number, and order of method calls, and can make Mock objects return specified values or throw specified exceptions
  • PowerMock: This tool is extended on EasyMock and Mockito to solve problems that EasyMock and Mockito cannot solve, such as static, final, and private methods. These features are not usually required to test well-architected code, but if you are adding unit tests to an existing project, you may have to use them if the old code has problems and cannot be changed
  • JMockit: JMockit is a lightweight mock framework designed to help developers write test programs with a set of tools and apis. The project is completely based on the Java.lang. instrument package of Java 5 SE and internally uses ASM libraries to modify Java Bytecode

Mockito has been widely used, so the emphasis here is on Mockito.

Mockito examples

Here we directly illustrate mockito’s help in unit testing with code that has three classes: the Person class:

public class Person {

    private final int    id;
    private final String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId(a) {
        return id;
    }

    public String getName(a) {
        returnname; }}Copy the code

PersonDAO:

public interface PersonDao {

    Person getPerson(int id);

    boolean update(Person person);
}
Copy the code

PersonService:

public class PersonService {

    private final PersonDao personDao;

    public PersonService(PersonDao personDao) {
        this.personDao = personDao;
    }

    public boolean update(int id, String name) {
        Person person = personDao.getPerson(id);
        if (person == null) {
            return false;
        }

        Person personUpdate = new Person(person.getId(), name);
        returnpersonDao.update(personUpdate); }}Copy the code

In this case, we are testing the Update method of the PersonService class. We find that the update method depends on the PersonDAO, and the PersonDAO is probably not fully developed during development, so when we test the PersonService, So how do you test the update method? How do you know if the connection is true or false? Here, we can think of unit testing as the idea that we want to test code without involving dependencies. This kind of testing lets you test the validity of your code regardless of its dependencies. The idea is that if the code works as designed and the dependencies work well, they should all work well at the same time. So we mock a PersonDAO object, and it doesn’t matter if the PersonDAO behavior performs as expected in the real world, such as whether the update succeeds or the query returns the correct data. The unit test for The PersonService only tests if there is a problem with its logic

Write the test code below:

public class PersonServiceTest {

    private PersonDao     mockDao;
    private PersonService personService;

    @Before
    public void setUp(a) throws Exception {
        // Impersonates the PersonDao object
        mockDao = mock(PersonDao.class);
        when(mockDao.getPerson(1)).thenReturn(new Person(1."Person1"));
        when(mockDao.update(isA(Person.class))).thenReturn(true);

        personService = new PersonService(mockDao);
    }

    @Test
    public void testUpdate(a) throws Exception {
        boolean result = personService.update(1."new name");
        assertTrue("must true", result);
        // Verify that getPerson(1) is executed once
        verify(mockDao, times(1)).getPerson(eq(1));
        // Verify that an update was performed
        verify(mockDao, times(1)).update(isA(Person.class));
    }

    @Test
    public void testUpdateNotFind(a) throws Exception {
        boolean result = personService.update(2."new name");
        assertFalse("must true", result);
        // Verify that getPerson(1) is executed once
        verify(mockDao, times(1)).getPerson(eq(1));
        // Verify that an update was performedverify(mockDao, never()).update(isA(Person.class)); }}Copy the code

We mock the PersonDAO and set stubbing, stubbing as follows:

  • When the getPerson method passes 1, it returns a Person object, otherwise it defaults to null
  • Return true when calling the update method

We tested two scenarios:

  • Update the name of Person with ID 1. Expect: Person to be found in DAO and updated successfully
  • Update the name of Person with ID 2, expected: Person cannot be found in DAO, update failed

Thus, according to the logic of the PersonService update method, after passing these two test cases, we think the code is fine. Mockito here plays the role of simulating DAO objects for us and helping us verify behavior (such as whether the getPerson and update methods are called)

Mockito Android Studio project configuration

Using Mockito in Android Studio is as simple as adding dependencies to your build.gradle file. As shown in figure:

dependencies{... testCompile'org. Mockito: mockito - core: 1.10.19'. }Copy the code

Mockito usage method

Use Mockito, and we have detailed API documentation, specific can view: site.mockito.org/mockito/doc… Below are some common uses of collation.

The validation behavior

Once created, mock records all interactions and you can verify anything you want

@Test
public void testVerify(a) throws Exception {
    //mock creation
    List mockedList = mock(List.class);

    //using mock object
    mockedList.add("one");
    mockedList.add("two");
    mockedList.add("two");
    mockedList.clear();

    //verification
    verify(mockedList).add("one");// Verify that the mockedList.add("one") method is called once. If not (0 or more), the test will fail
    verify(mockedList, times(2)).add("two");
    // Verify that the mockedList.add("two") method is called twice. If not, the test will fail
    verify(mockedList).clear();// Verify that the mockedList.clear() method is called once. If not (0 times or more), the test fails
}
Copy the code

Stubbing

@Test
public void testStubbing(a) throws Exception {
    // You can mock concrete classes, not just interfaces
    LinkedList mockedList = mock(LinkedList.class);

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

    / / print "first"
    System.out.println(mockedList.get(0));

    // This throws a Runtime exception
    System.out.println(mockedList.get(1));

    // This will print "null" because get(999) is not set
    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 stubbing, there are the following points to note:

  • Mock returns null, empty collection, and default values by default for methods that return values. For example, return 0 for int/Integer and false for Boolean /Boolean
  • Stubbing can be overwritten, but be aware that overwriting existing stubbing may not be good
  • Once stubbing, the method always returns the value of stubbing, no matter how many times it is called
  • When you stubbing the same method multiple times, the last stubbing is the most important

The parameter match

@Test
public void testArgumentMatcher(a) throws Exception {
    LinkedList mockedList = mock(LinkedList.class);
    // Stub with the built-in parameter matcher
    when(mockedList.get(anyInt())).thenReturn("element");

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

    // You can also use the parameter matcher to verify that this test passes
    verify(mockedList).get(anyInt());
    // The test will fail here because get(33) is not called
    verify(mockedList).get(eq(33));
}
Copy the code

If you use a parameter matcher, you should use a parameter matcher for all parameters

 verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
 // This is correct because eq returns the parameter matcher

 verify(mock).someMethod(anyInt(), anyString(), "third argument");
 // An exception will be thrown because the third argument is not a parameter matcher. Once the parameter matcher is used to verify, all arguments should use parameter matching
Copy the code

Verify the exact number of calls, most, least, never, etc

@Test
public void testInvocationTimes(a) throws Exception {

    LinkedList mockedList = mock(LinkedList.class);

    //using mock
    mockedList.add("once");

    mockedList.add("twice");
    mockedList.add("twice");

    mockedList.add("three times");
    mockedList.add("three times");
    mockedList.add("three times");

    // Use times(1) by default.
    verify(mockedList).add("once");
    verify(mockedList, times(1)).add("once");

    // Verify the exact number of calls
    verify(mockedList, times(2)).add("twice");
    verify(mockedList, times(3)).add("three times");

    Never () is an alias for times(0)
    verify(mockedList, never()).add("never happened");

    // Verify with atLeast()/atMost()
    verify(mockedList, atLeastOnce()).add("three times");
    // The following sentence will not pass the test
    verify(mockedList, atLeast(2)).add("five times");
    verify(mockedList, atMost(5)).add("three times");
}
Copy the code

Throw an exception for the void method

@Test
public void testVoidMethodsWithExceptions(a) throws Exception {

    LinkedList mockedList = mock(LinkedList.class);
    doThrow(new RuntimeException()).when(mockedList).clear();
    // RuntimeException will be thrown
    mockedList.clear();
}
Copy the code

Verify the call order

@Test
public void testVerificationInOrder(a) throws Exception {
    // A. Single mock whose methods must be invoked in a particular order
    List singleMock = mock(List.class);

    // Use a single mock object
    singleMock.add("was added first");
    singleMock.add("was added second");

    / / create inOrder
    InOrder inOrder = inOrder(singleMock);

    Singlemock. add("was added first") is called first
    inOrder.verify(singleMock).add("was added first");
    inOrder.verify(singleMock).add("was added second");

    // Multiple mock objects
    List firstMock = mock(List.class);
    List secondMock = mock(List.class);

    //using mocks
    firstMock.add("was called first");
    secondMock.add("was called second");

    // Create inorders for multiple mock objects
    inOrder = inOrder(firstMock, secondMock);

    // Verify that firstMock precedes secondMock calls
    inOrder.verify(firstMock).add("was called first");
    inOrder.verify(secondMock).add("was called second");
}
Copy the code

Verify that the mock object has no interaction

@Test
public void testInteractionNeverHappened(a) {
    List mockOne = mock(List.class);
    List mockTwo = mock(List.class);

    // The test passes
    verifyZeroInteractions(mockOne, mockTwo);

    mockOne.add("");
    // The test failed because mockTwo already interacted
    verifyZeroInteractions(mockOne, mockTwo);
}
Copy the code

Find if there are any unauthenticated interactions

It is not recommended to use it too much. Some users who did a lot of classic, expect-run-verify mocking tend to use verifyNoMoreInteractions() very often, even in every test method. verifyNoMoreInteractions() is not recommended to use in every test method. verifyNoMoreInteractions() is a handy assertion from the interaction testing toolkit. Use it only when it’s relevant. Abusing it leads to overspecified, less maintainable tests.

@Test
public void testFindingRedundantInvocations(a) throws Exception {
    List mockedList = mock(List.class);
    //using mocks
    mockedList.add("one");
    mockedList.add("two");

    verify(mockedList).add("one");

    // Validation failed because mockedList.add("two") has not been validated
    verifyNoMoreInteractions(mockedList);
}
Copy the code

@ Mock annotations

  • Reduce code
  • Enhanced readability
  • Make Verify error messages more readable because variable names can be used to describe marking mock objects
public class MockTest {

    @Mock
    List<String> mockedList;

    @Before
    public void initMocks(a) {
        // Must, otherwise the annotation is invalid
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testMock(a) throws Exception {
        mockedList.add("one");
        verify(mockedList).add("one"); }}Copy the code

Set different stubbing depending on the call order

private interface MockTest {
    String someMethod(String arg);
}

@Test
public void testStubbingConsecutiveCalls(a) throws Exception {

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

    // First call, throw RuntimeException
    mock.someMethod("some arg");

    // The second call returns foo
    System.out.println(mock.someMethod("some arg"));

    // Continue the call and return "foo", whichever is the last stub
    System.out.println(mock.someMethod("some arg"));

    // Here is a more concise way to write it
    when(mock.someMethod("some arg")).thenReturn("one"."two"."three");
}
Copy the code

DoReturn () | doThrow () | doAnswer () | doNothing () | doCallRealMethod usage (), etc

@Test
public void testDoXXX(a) throws Exception {
    List mockedList = mock(List.class);
    doThrow(new RuntimeException()).when(mockedList).clear();
    // An exception will be thrown
    mockedList.clear();
}
Copy the code

Spy monitors the real object

  • Spy creates a copy, and if you keep the original list and operate with it, spy will not detect the interaction
  • Spy a real object + trying to stub a final method can be problematic
@Test
public void testSpy(a) throws Exception {
    List list = new LinkedList();
    List spy = spy(list);

    // Optionally, you can stub some methods
    when(spy.size()).thenReturn(100);

    // Call the "real" method
    spy.add("one");
    spy.add("two");

    / / print one
    System.out.println(spy.get(0));

    // the size() method is stubbed, printing 100
    System.out.println(spy.size());

    // Optionally, verify the behavior of the Spy object
    verify(spy).add("one");
    verify(spy).add("two");

    / / the following writing has a problem, spy. Get (10) are IndexOutOfBoundsException anomalies
    when(spy.get(10)).thenReturn("foo");
    // The following methods can be used
    doReturn("foo").when(spy).get(10);
}
Copy the code

Sets the default return value for unstub methods

@Test
public void testDefaultValue(a) throws Exception {

    List listOne = mock(List.class, Mockito.RETURNS_SMART_NULLS);
    List listTwo = mock(List.class, new Answer() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {

            // TODO: 2016/6/13 return default value here
            return null; }}); }Copy the code

Parameters to capture

@Test
public void testCapturingArguments(a) throws Exception {
    List mockedList = mock(List.class);
    ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
    mockedList.add("John");
    // Capture parameters after validation
    verify(mockedList).add(argument.capture());
    // Validate parameters
    assertEquals("John", argument.getValue());
}
Copy the code

Real partial simulation

    //you can create partial mock with spy() method:
    List list = spy(new LinkedList());

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

Reset the away,

Don’t harm yourself. reset() in the middle of the test method is a code smell (you’re probably testing too much).

@Test
public void testReset(a) throws Exception {
    List mock = mock(List.class);
    when(mock.size()).thenReturn(10);
    mock.add(1);
    reset(mock);
    // From here, all previous interactions and stubs are invalidated
}
Copy the code

Serializable mocks

WARNING: This should be rarely used in unit testing.

@Test
public void testSerializableMocks(a) throws Exception {
    List serializableMock = mock(List.class, withSettings().serializable());
}
Copy the code

More notes: @captor, @Spy, @Injectmocks

  • Create ArgumentCaptor @ Captor
  • @spy can replace Spy (Object).
  • @InjectMocks If the annotation declares a variable that requires a mock object, Mockito will automatically inject a mock or spy member
// You can write it like this
@Spy BeerDrinker drinker = new BeerDrinker();
// You can also write that Mockito automatically instantiates Drinker
@Spy BeerDrinker drinker;

// automatically instantiate
@InjectMocks LocalPub;
Copy the code

Timeout validation

private interface TimeMockTest {
    void someMethod(a);
}

@Test
public void testTimeout(a) throws Exception {

    TimeMockTest mock = mock(TimeMockTest.class);
    SomeMethod () {someMethod() {someMethod();
    verify(mock, timeout(100)).someMethod();
    // This is equivalent to the above code
    verify(mock, timeout(100).times(1)).someMethod();

    // block for 100ms, and verify that it is exactly 2 times executed at timeout
    verify(mock, timeout(100).times(2)).someMethod();

    // Validate at least 2 times with timeout
    verify(mock, timeout(100).atLeast(2)).someMethod();

    // Verify someMethod() with custom validation mode after timeout
    VerificationMode yourOwnVerificationMode = new VerificationMode() {
        @Override
        public void verify(VerificationData data) {
            // TODO: 2016/12/4 implement me}}; verify(mock,new Timeout(100, yourOwnVerificationMode)).someMethod();
}
Copy the code

Check for mock or spy

   Mockito.mockingDetails(someObject).isMock();
   Mockito.mockingDetails(someObject).isSpy();
Copy the code