Unit testing tool Mockito framework

What is mock

MockThe Chinese translation of Chinese is imitation, simulation, and false. For the testing framework, a simulated/fake object is constructed so that our testing can proceed smoothly.

MockDuring the test, the test is, for some not easy to construct (as HttpServletRequestmust in Servletorder to construct out of the container) or not easy to get more complex objects (such as JDBCthe ResultSettarget), with a virtual object ( Mockthe object) to create, in order to testing method.

Why use mock testing

Unit testing is to verify the correctness of our code, we focus on the code flow and the correctness of the results.

Contrast real running code, some of which may be external dependencies build steps relatively trouble, if we construct the building in accordance with the rules of external dependencies real code, will greatly increase the operating unit testing, code would also mingled much part of the non-testing The content and test cases are complicated and difficult to understand.

Adopt Mockframework, we can virtual an external dependencies , focus only on the code of the process and result , truly testing purposes.

Benefits of mock testing framework

  1. A complex object can be virtualized very simply (for example, an implementation class of an interface can be virtualized);
  2. You can configure mockthe behavior of the object;
  3. Can make test cases only focus on the test process and results;
  4. Reduce the coupling of external classes, systems and dependencies to unit testing.

Mockito process


As shown, using the Mockitogeneral process is as follows:

  1. Create external dependencies of Mockthe object, and this Mockis injected into the subject test class in;

  2. Execute test code ;

  3. Check test code has been executed correctly.

Use of Mockito

In Modulethe build.gradlemiddle add the following:

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

Here is a little explanation:

  • mockito-core: A local unit testing , the test code is located in the pathmodule-name/src/test/java/
  • mockito-android: For device testing , the need to run androiddevice for testing, the test code is located in the pathmodule-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

Mockito usage example

Common unit test use mockito(mockito-core), path:module-name/src/test/java/

Here is an excerpt from the official website Demo:

Check whether the related behavior of the tuning object is called

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"); //调用了add("one")行为
mockedList.clear(); //调用了clear()行为

// Selective, explicit, highly readable verification
verify(mockedList).add("one"); // 检验add("one")是否已被调用
verify(mockedList).clear(); // 检验clear()是否已被调用

Here mocka List(here just to use as Demoan example, usually to Listcreate this simple class object, directly and newa real object can be, without the need for mock), verify()whether a test subject in front of the relevant acts have been performed here mockedListin verifybefore has been performed add("one")and clear()behavior, it 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));

Here are a few more important points:

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

This sentence Mockitoresolves as follows: When the object mockedListinvocation get()method, and parameters 0, the result is returned "first", which is equivalent to customize our mockresults of behavior of objects ( mock LinkedListobject mockedList, specify its behavior get(0), the result returned is "first").

mockedList.get(999)

Because mockedListit does not specify get(999)the behavior, so its results null. Because the Mockitounderlying principle is to use cglibdynamically generate a proxy class object , therefore, mockout of which the object is a substance of the agent , the agent is not configured / acts specified in the case of default returns a null value .

Above Demousing a static method mock() to simulate an example, we can also annotate @Mockalso simulate an example:

@Mock
private Intent mIntent;

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

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

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

For marked with @Mock, @Spy, @InjectMocksetc. annotated member variable initialization so far there are 2ways to:

  1. To JUnitadd a test class@RunWith(MockitoJUnitRunner.class)

  2. In marked with @Beforeinitialization method is called within the method:MockitoAnnotations.initMocks(Object)

The above test case for the @Mockinitialization of member variables notes now have one more way MockitoRule. Rules MockitoRulewill automatically help us call MockitoAnnotations.initMocks(this)to instantiate an annotation member variables, we do not need to manually initialized.

Important methods of Mockito

Instantiate virtual objects

// 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);
  • For all methods, mockobjects default return null, the original type / types of packaging original default value, or an empty set . For example, int/Integertype, returns 0, for boolean/Booleanreturn false.

  • Behavior configuration ( stub) can be copied: for example, the usual object behavior has a certain configuration, but the test method can copy this behavior. Keep in mind that behavioral replication may indicate too many potential behaviors.

  • Once you have configured behavior, always returns configuration values , regardless of the method has been called many times.

  • The last behavior configuration is more important. When you configure the same method with the same parameters many times, the last time it works.

Parameter matching

MockitoParameter object equals()to verify the parameters are consistent method, when more flexibility is required, matcher parameters may be used:

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

Matcher parameters allow a more flexible verification and behavior configuration . More built matcher and custom parameter matcher example refer to: ArgumentMatchers,MockitoHamcrest

Note: If a parameter matcher is used, then all parameters need to provide 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.

Similarly anyObject(), eq()this type of matcher does not return matching values. They internal record a match stack and returns a null value (typically null). This implementation is to match javathe compiler static type safety , the consequences of doing so is that you can not test / configuration method to use outside anyObject(), eq()and other methods.

Check times

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");

The commonly used methods of checking times are as follows:

Method Meaning
times(n) The number of times is n, the default is 1 (times(1))
never() The number of times is 0, which is equivalent to times(0)
atLeast(n) At least n times
atLeastOnce() At least once
atMost(n) Up to n times

Throw an exception

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

Check in order

Sometimes for some behaviors, there is a priority, so when we verify, we need to consider the sequence of this behavior:

// 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");

Stub continuous call

For the same way, if we want it in multiple calls respectively return different values, then you can use stub continuous call:

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"));

You can also use the following more concise stub continuous call method:

when(mock.someMethod("some arg")).thenReturn("one", "two", "three");

Note: Continuous stub calls require chained calls. If multiple stub configurations of the same method are used, only the last one will work (override 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")

Function without return value

For the return type of voidthe method, a stub for an alternative form of when(Object)a function, because the compiler can not claim the presence of parentheses voidmethod.

For example, a stub for the return type voidis thrown when a method requires a call to an exception:

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

Monitor real objects

We are used before mockout an object. Thus, when there is no configuration / stub specific behavior, the result will be returned empty type . And if a spy Object ( spy), then for no stub behavior, it will call the original target method. It can be spythought of 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");

Note: Because spy is a partial mock, sometimes when (Object) is used, it cannot be stubbed. At this point, you can consider using doReturn() | Answer() | Throw() for stubbing:

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

spyNot the real object of the agent . Instead, it passed over the real object were cloned . So, for the real object of any operation, spythe object does not perceive. Similarly, for spyany operation object, it will not affect the real object .

Of course, if mockfor the object locally mock , by the doCallRealMethod() | thenCallRealMethod()method it is also possible:

// 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();

Test Driven Development

To behavior-driven development of the format // given // when // then write test cases for testing usage cornerstone comment, this is the Mockitoofficial method for the preparation of test cases, the test is strongly recommended to use this way to write.

 import static org.mockito.BDDMockito.*;

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

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

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();

If you exchange experience in software testing, interface testing, automated testing, and interviews. If you are interested, you can add software test communication: 1085991341, and there will be technical exchanges with colleagues.

@InjectMock

Constructors, methods, member variables dependency injection @InjectMockof the annotation, Mockitochecks the class constructors , methods , or member variable , depending on their type automatically mock.

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() {
        // 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();
            this.user = user;
            this.database = database;
        }

        public void initialize() {
            database.addListener(new ArticleListener());
        }
    }

    public static class User {
    }

    public static class ArticleListener {
    }

    public static class ArticleDatabase {
        public void addListener(ArticleListener listener) {
        }
    }
}

Member variable managerof type ArticleManager, it's Farewell identified above @InjectMocks. This means mockthat manager, Mockitoyou need to automatically mockthe ArticleManagerrequired configuration parameters (ie: userand database), finally mockget an ArticleManagerassignment to manager.

Parameter capture

ArgumentCaptorAllow verifytime to obtain the method parameter content , which enables us in the testing process can pair calls method parameters were captured and tested .

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Captor
private ArgumentCaptor<List<String>> captor;
@Test
public void testArgumentCaptor(){
    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"));
}

Limitations of Mocktio

  1. Not mocka static method;
  2. Not mockbuilder;
  3. Can not mock equals()and hashCode()method
    above is the entire contents of the above Benpian want to help you, have helped to friends welcome thumbs up, comment.

Guess you like

Origin blog.csdn.net/Chaqian/article/details/106673465