Фреймворк для модульного тестирования Mockito

Mockito
Фреймворк для модульного тестирования Mockito

Обзор

Mockito — это мок-фреймворк для модульного тестирования в Java.

Внедрение pom-зависимостей

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>LATEST</version>
</dependency>

Включить Мокито

Создать фиктивный объект через параметр класса (класс, интерфейс), который отличается от реального созданного объекта. Серия статических методов, использующих класс Mockito.

public static <T> T mock(Class <T> classToMock)

Напишите пример Мокито

Mockito должен зависеть от Junit.

package org.byron4j.cookbook.mocketio.basic;

import org.junit.Test;
import org.mockito.Mockito;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;

public class MockitoAnnotationTest {

    @Test
    public void whenNotUseMockAnnotation_thenCorrect() {
        // 创建一个mock出来的ArrayList对象
        List mockList = Mockito.mock(ArrayList.class);

        // 调用mock对象的方法
        mockList.add("one");
        //mockList.add("one");

        // 获取mock对象的实际方法,获取size,结果为0
        System.out.println("mockList.size(): " + mockList.size());
        // toString方法
        System.out.println("mockList's toString is: " + mockList);

        // 验证mock对象mockList的add方法是否被调用了一次
        Mockito.verify(mockList).add("one");
        assertEquals(0, mockList.size());

        // 当调用mockList.size()的时候,总是返回100
        Mockito.when(mockList.size()).thenReturn(100);

        assertEquals(100, mockList.size());
    }
}

Результат работы:

mockList.size(): 0
mockList's toString is: Mock for ArrayList, hashCode: 409962262
  • использоватьList mockList = Mockito.mock(ArrayList.class);Создайте макет объекта ArrayList mockList
  • Вызвать метод фиктивного объектаmockList.add("one");
  • тогда позвониmockList.size()Результат равен 0, что указывает на то, чтоmockList.add("one");Код указывает только на то, что произошло само поведение добавления, и не повлияет на другое поведение mockList.
  • Метод проверки, который проверяет, был ли один раз вызван метод добавления фиктивного объекта mockList.
Mockito.verify(mockList).add("one");
assertEquals(0, mockList.size());
  • При вызове mockList.size() он всегда возвращает 100
Mockito.when(mockList.size()).thenReturn(100);
assertEquals(100, mockList.size());

проверить метод

Убедитесь, что включенное поведение (метод) происходит один раз (вызывается один раз), т. е. verify(mock, times(1)), например:verify(mock).someMethod("some arg");. равноverify(mock, times(1)).someMethod("some arg");

Mockito.verify(mockList).add("one");

равно

Mockito.verify(mockList, Mockito.times(1)).add("one");

Mockito.times(1)Параметр 1 указывает, что ожидаемое количество выполнений равно 1.

Метод проверки передает два параметра: фиктивный объект, режим проверки.
public static <T> T verify(T mock,  VerificationMode mode);

Mockito.times(int WantedNumberOfInvocations) может получить объект VerificationMode, который на самом деле вызываетVerificationModeFactory.times(wantedNumberOfInvocations)Получить объект Times:new Times(wantedNumberOfInvocations), Times реализует интерфейс VerificationMode.

  • Параметр 1: фиктивный объект, обязательный

  • Параметр 2: Режим проверки: times(x), atLeastOnce() или never() и т. д.; если это times(1), этот параметр можно игнорировать.

Стек вызовов метода times выглядит следующим образом:

org.mockito.Mockito#times(int wantedNumberOfInvocations)
	org.mockito.internal.verification.VerificationModeFactory#times(int wantedNumberOfInvocations)
		org.mockito.internal.verification.Times(int wantedNumberOfInvocations)

когда метод

Метод Mockito.when определяется следующим образом:

public static <T> OngoingStubbing<T> when(T methodCall)

Методу when необходимо передать вызов метода фиктивного объекта.Например, в этом примере мы передаем вызов метода mockList.size() фиктивного объекта mockList. Метод when оставляет заглушку и вызывается обратно, когда мы хотим, чтобы макет возвращал определенное возвращаемое значение в определенной ситуации. Простое намерение:当x方法调用的时候,就返回y.

Пример:

  • when(mock.someMethod()).thenReturn(10);: возвращает 10 при вызове метода

  • when(mock.someMethod(anyString())).thenReturn(10);: гибкие параметры

  • when(mock.someMethod("some arg")).thenThrow(new RuntimeException());: при вызове метода выбрасывается исключение

  • when(mock.someMethod("some arg")).thenThrow(new RuntimeException()).thenReturn("foo");: последовательные вызовы разного поведения

  • when(mock.someMethod("some arg")).thenReturn("one", "two");: последовательные заглушки, первый вызов возвращает «один», второй и последующие вызовы возвращают «два».

  • when(mock.someMethod("some arg")).thenReturn("one").thenReturn("two");: имеет тот же эффект, что и предыдущий

  • when(mock.someMethod("some arg")).thenThrow(new RuntimeException(), new NullPointerException();: непрерывная заглушка, исключение исключения

@Test
public void whenTest() {
    List mock = Mockito.mock(List.class);
    Mockito.when(mock.size()).thenReturn(-1);
    System.out.println("mock.size():" + mock.size());



    // 连续存根
    Mockito.when(mock.size()).thenReturn(1).thenReturn(2).thenReturn(3);
    for(int i=1; i <= 5; i++){
        System.out.println("=====连续存根方式1:=====: " + mock.size());
    }

    Mockito.when(mock.size()).thenReturn(1,2, 3);
    for(int i=1; i <= 5; i++){
        System.out.println("#####连续存根方式2:#####: " + mock.size());
    }

    // 模拟异常
    Mockito.when(mock.size()).thenThrow(new RuntimeException(), new NullPointerException());
    try{
        mock.size();
    }catch (Exception e){
        System.out.println(e);
    }
    try{
        mock.size();
    }catch (Exception e){
        System.out.println(e);
    }

}

Рабочий вывод:

mock.size():-1
=====连续存根方式1:=====: 1
=====连续存根方式1:=====: 2
=====连续存根方式1:=====: 3
=====连续存根方式1:=====: 3
=====连续存根方式1:=====: 3
#####连续存根方式2:#####: 1
#####连续存根方式2:#####: 2
#####连续存根方式2:#####: 3
#####连续存根方式2:#####: 3
#####连续存根方式2:#####: 3
java.lang.RuntimeException
java.lang.NullPointerException

Включить функцию аннотации Mockito

@RunWith(MockitoJUnitRunner.class) Включить функцию аннотации

использовать@RunWith(MockitoJUnitRunner.class)Включите функцию аннотации Mockito в классе.

@RunWith(MockitoJUnitRunner.class)
public class MockitoAnnotationStartup {
}

@Мок-аннотация

Мок-объект можно получить с помощью аннотации @Mock, которая эквивалентна Mockito.mock(class).

    /**注解得到的mock对象*/
    @Mock
    List<String> mockList;
    
    等价于
    
    List<String> mock = Mockito.mock(List.class);

Пример выглядит следующим образом:

package org.byron4j.cookbook.mocketio.basic;

import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;

import java.util.List;

import static org.junit.Assert.assertEquals;

public class MockitoAnnoTest extends MockitoAnnotationStartup{

    /**注解得到的mock对象*/
    @Mock
    List<String> mockList;

    @Test
    public void testRaw(){
        List<String> mock = Mockito.mock(List.class);
        mock.add("one");
        mock.add("one");
        Mockito.verify(mock, Mockito.times(2)).add("one");

        Mockito.when(mock.size()).thenReturn(100);
        assertEquals(100, mock.size());


    }

    @Test
    public void testAnno(){
        mockList.add("one");
        mockList.add("one");
        Mockito.verify(mockList, Mockito.times(2)).add("one");

        Mockito.when(mockList.size()).thenReturn(100);
        assertEquals(100, mockList.size());


    }
}

@Шпионская аннотация

Подобно аннотации @Mock, существует также аннотация @Spy. шпион - это значение шпиона, шпиона, подделки.

List<String> mock = Mockito.spy(List.class);

использовать аннотации

@Spy
List<String> spyList;

Аннотация @Captor (захват параметров)

Захватчик параметров ArgumentCaptor соответствует аннотации @Captor.

Оригинальный способ создания перехватчика параметров:

@Test
public void whenNotUseCaptorAnnotation_thenCorrect() {
    List mockList = Mockito.mock(List.class);
    ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
 
    mockList.add("one");
    Mockito.verify(mockList).add(arg.capture());
 
    assertEquals("one", arg.getValue());
}

Создайте захват параметров с аннотацией @Captor:

@Mock
List mockedList;
 
@Captor
ArgumentCaptor argCaptor;
 
@Test
public void whenUseCaptorAnnotation_thenTheSam() {
    mockedList.add("one");
    Mockito.verify(mockedList).add(argCaptor.capture());
 
    assertEquals("one", argCaptor.getValue());
}
  • ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);: создать перехватчик параметров
  • Mockito.verify(mockedList).add(argCaptor.capture());: Используйте метод захвата в проверке для захвата параметров добавления метода; метод захвата должен быть в проверке.
  • argCaptor.getValue(): получить параметры, захваченные устройством захвата параметров.

@InjectMocks аннотация

@InjectMocksАннотации могут автоматически внедрять фиктивные свойства в тестовый объект.

@Mock
Map<String, String> wordMap;

@InjectMocks
MyDictionary myDictionary = new MyDictionary();


@Test
    public void testInjectMocks(){
        Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");

        assertEquals("aMeaning", myDictionary.getMeaning("aWord"));

        System.out.println(myDictionary.getMeaning("aWord"));
    }

    class MyDictionary{
        Map<String, String> wordMap;

        public String getMeaning(String word){
            return wordMap.get(word);
        }
    }
  • Класс MyDictionary имеет свойство wordMap:Map<String, String> wordMap;
  • Смоделируйте переменную с именем wordMap :
@Mock
Map<String, String> wordMap;
  • Отметьте его аннотацией @InjectMocks:
@InjectMocks
MyDictionary myDictionary = new MyDictionary();

Поддельный объект wordMap будет внедрен в одноименное свойство экземпляра myDictionary.

Меры предосторожности:

Используйте аннотации, чтобы свести к минимуму дублирование кода для создания фиктивных объектов.

Используйте аннотации, чтобы сделать тестовые случаи более читабельными

Вставка объектов @Spy и @Mock с помощью аннотации @InjectMocks

Использованная литература: