Инструмент модульного тестирования Mockito framework

задняя часть контрольная работа модульный тест Mockito

предисловие

Mockitoв настоящее время является самым популярныммодульный тест MockРамка. использоватьMockрамки, мы можемвиртуальныйиз одноговнешние зависимости, нижний тесткомпонентымеждуСвязь, ориентируясь только на кодПроцесс и результаты, чтобы действительно достичь цели тестирования.

текст

Что такое мок

MockКитайский перевод — имитация, симуляция, ложь. Для среды тестирования это создание фиктивного/поддельного объекта, чтобы наши тесты могли проходить гладко.

MockТестирование находится в процессе тестирования, для некоторыхне легко построить(какHttpServletRequestДолжен бытьServletконтейнер можно только построить) или его нелегко получитьдовольно сложныйобъект (например,JDBCсерединаResultSetобъект), используйтевиртуальныйОбъект(Mockобъект) создать для проверки метода.

Зачем использовать пробное тестирование

модульный тестЧтобы проверить правильность нашего кода, мы фокусируемся на процессе кода и правильности результатов.

По сравнению с реальным работающим кодом некоторые из них могут бытьвнешние зависимостиЭтапы конструирования относительно громоздки, и если мы по-прежнему строим внешние зависимости по правилам конструирования реального кода, то работа юнит-тестирования будет сильно увеличена, а код будет перемешан со слишком большим количеством нетестовых частей, а тест случаи будут сложными и трудными для понимания.

использоватьMockрамки, мы можемвиртуальныйиз одноговнешние зависимости, ориентируясь только на кодПроцесс и результаты, чтобы действительно достичь цели тестирования.

Преимущества фреймворков для пробного тестирования

  1. Очень просто виртуализировать сложный объект (например, виртуализировать класс реализации интерфейса);
  2. можно настроитьmockповедение объекта;
  3. Тестовые примеры можно сделать так, чтобы они фокусировались только на процессе и результатах тестирования;
  4. Уменьшите связь, которую внешние классы, системы и зависимости привносят в модульное тестирование.

Процесс Мокито

Как показано, используйтеMockitoОбщий процесс выглядит следующим образом:

  1. Создайтевнешние зависимостиизMockобъект, то этоMockобъект, введенный втестовый класссередина;

  2. воплощать в жизньтестовый код;

  3. чектестовый кодПравильно ли он выполнен.

Использование Мокито

существуетModuleизbuild.gradleДобавьте следующее:

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

Вот небольшое пояснение:

  • mockito-core: заТест местного блока, путь тестового кода которого находится вmodule-name/src/test/java/
  • mockito-android: заИспытание оборудования, т.е. нужно запуститьandroidтестируемое устройство с помощью пути тестового кода по адресуmodule-name/src/androidTest/java/

Последнюю версию mockito-core можно запросить в Maven: mockito-core. Последнюю версию mockito-android можно запросить в Maven: mockito-android

Пример использования Мокито

Обычное использование модульных тестовmockito(mockito-core),дорожка:module-name/src/test/java/

Вот выдержка с официального сайтаDemo:

Проверьте, вызывается ли связанное поведение вызывающего объекта

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()是否已被调用

здесьmockвзял одинList(Это только для использования в качествеDemoнапример, обычно дляListС точки зрения создания этого простого объекта класса, прямоеnewПодойдет настоящий объект, нет необходимостиmock),verify()Проверяет, выполнял ли объект соответствующее поведение раньше, здесьmockedListсуществуетverifyбыло выполнено раньшеadd("one")иclear()поведение, поэтомуverify()пройдет.

поведение конфигурации/метода

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

Вот несколько важных моментов для анализа:

when(mockedList.get(0)).thenReturn("first")

Это предложениеMockitoРазрешит: когда объектmockedListперечислитьget()метод, а параметры0, возвращаемый результат"first", что эквивалентно настройке нашегоmockРезультат поведения объекта (mock LinkedListобъектmockedList, задающий его поведениеget(0), возвращаемый результат"first").

mockedList.get(999)

так какmockedListне определенget(999)поведение, поэтому результатnull. так какMockitoОсновной принцип заключается в использованииcglibдинамически генерироватьобъект прокси-класса,следовательно,mockОбъект, который выходит, по существуиграет роль, агент находится вНет настроенного/указанного поведенияВ случае возврата по умолчаниюнулевой.

вышеDemoиспользуетстатический метод mock()Для моделирования экземпляра мы также можем использовать аннотации@MockТакже смоделируйте экземпляр:

@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;
}

для отмеченных@Mock, @Spy, @InjectMocksи т. д. Аннотированные переменные-членыинициализацияПока есть2методы:

  1. правильноJUnitдобавлен тестовый класс@RunWith(MockitoJUnitRunner.class)

  2. отмечены@BeforeВызовите метод инициализации внутри метода:MockitoAnnotations.initMocks(Object)

Приведенный выше тестовый пример для@MockСуществует еще один способ инициализировать переменные-члены аннотаций.MockitoRule. правилоMockitoRuleавтоматически вызовет насMockitoAnnotations.initMocks(this)идти ссоздавать экземплярвнеаннотацияпеременные-члены, нам не нужно инициализировать их вручную.

Важные методы Mockito

Создание виртуальных объектов

// 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);
  • Для всех методов,mockОбъект возвращается по умолчаниюnull,Примитивные типы / Обертки примитивных типовзначение по умолчанию илипустая коллекция. например, дляint/Integerтип, возврат0,заboolean/Booleanзатем вернутьсяfalse.

  • конфигурация поведения (stub) можно переопределить: например, обычное поведение объекта имеет определенную конфигурацию, но тестовый метод может переопределить это поведение. Имейте в виду, что поведенческие переопределения могут указывать на слишком много потенциальных вариантов поведения.

  • После настройки поведения метод всегда будет возвращатьзначение конфигурации, независимо от того, сколько раз вызывается метод.

  • Последняя конфигурация поведения более важна, когда вы настраиваете один и тот же метод с одними и теми же параметрами много раз, работает последняя.

сопоставление параметров

Mockitoчерез объект параметраequals()метод для проверки согласованности параметров, а когда требуется большая гибкость, можно использовать средство сопоставления параметров:

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

сопоставитель параметровобеспечивает большую гибкостьпроверятьиконфигурация поведения. БолееВстроенный сопоставительисопоставитель пользовательских параметровНапример, см.:ArgumentMatchers,MockitoHamcrest

Примечание. Если используются средства сопоставления параметров, то все параметры должны предоставлять средство сопоставления параметров.

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.

похожийanyObject(),eq()Этот тип сопоставления не возвращает совпадающие значения. Они записываютстек сопоставленияи возвращает нулевое значение (обычноnull). Эта реализация должна соответствоватьjavaсоставительбезопасность статического типа, следствием этого является то, что вы не можетеМетоды проверки/конфигурациивнешнее использованиеanyObject(),eq()и другие методы.

Время проверки

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

Обычно используются следующие методы проверки времени:

Method Meaning
times(n) Количество раз n, по умолчанию 1 (раз(1))
never() Количество раз равно 0, что эквивалентно разу (0)
atLeast(n) не менее n раз
atLeastOnce() Хотя бы один раз
atMost(n) не более n раз

Выбросить исключение

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

Проверьте в порядке

Иногда для некоторых поведений существует последовательность, поэтому при проверке нам нужно учитывать последовательность этого поведения:

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

Заглушки непрерывных вызовов

Для того же метода, если мы хотим его внесколько вызововсреднийвозвращать разныезначение, то вы можете использовать заглушки для выполнения последовательных вызовов:

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

Этот метод также можно вызывать непрерывно, используя следующую более краткую заглушку:

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

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

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

функция без возвращаемого значения

завозвращаемый типзаvoidметод, заглушка требует использования другой формыwhen(Object)функция, потому что компилятор требует, чтобы круглые скобки не могли существоватьvoidметод.

Например, заглушить возвращаемый типvoidметод, который требуется для создания исключения при вызове:

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

следить за реальными объектами

бывшего употребленияmockиз объекта. Таким образом, когданет конфига/заглушкиЕсли его конкретное поведение, результат будет возвращенпустой тип. тогда как если вы используетешпионский объект(spy), то длянет заглушкиповедение, он вызоветисходный объектМетоды. можно поставитьspyпредставить местный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");

Примечание. Поскольку шпион является локальным макетом, иногда при использовании (Object) его нельзя заглушить. На этом этапе рассмотрите возможность заглушки с помощью таких методов, как doReturn() | Answer() | Throw() :

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

spyнетреальный объектизиграет роль. Наоборот, этореальный объектпровестиклон. Так дареальный объектлюбая операция,spyСубъект этого не воспринимает. то же самое, даspyЛюбая эксплуатация объекта не повлияет нареальный объект.

Конечно, если вы используетеmockобъектместный mock,пройти черезdoCallRealMethod() | thenCallRealMethod()также возможен метод:

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

Разработка через тестирование

отповеденческое развитиеФормат использует аннотацию //given //when //then для написания тестовых случаев в качестве краеугольного камня использования тестов, что и являетсяMockitoОфициальный метод написания тестовых случаев, настоятельно рекомендуется использовать этот метод для написания тестов.

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

Выходная информация пользовательской проверки ошибок

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

@InjectMock

Конструктор, метод, внедрение зависимостей переменных-членов использовать@InjectMockПри аннотации,Mockitoпроверюконструктор класса,методилиПеременные-члены, согласно ихтипсделать автоматическим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) {
        }
    }
}

Переменные-членыmanagerТипArticleManager, его верхняя метка идентифицирует@InjectMocks. Это означаетmockвнеmanager,Mockitoнужно автоматическиmockвнеArticleManagerнужныйСтроительные параметры(который:userиdatabase),наконец-тоmockполучить одинArticleManager, назначенныйmanager.

захват параметров

ArgumentCaptorразрешено вverifyполучить, когдаСодержимое параметра метода, что позволяет намПроцесс тестированияЧжуннэнпараметры метода вызовапровестипойматьиконтрольная работа.

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

Ограничения Мокито

  1. не можетmockстатический метод;
  2. не можетmockконструктор;
  3. не можетmock equals()иhashCode()метод.

Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack

零壹技术栈

Эта учетная запись будет продолжать делиться сухими товарами серверных технологий, включая основы виртуальных машин, многопоточное программирование, высокопроизводительные фреймворки, асинхронное ПО, промежуточное ПО для кэширования и обмена сообщениями, распределенные и микросервисы, материалы для обучения архитектуре и расширенные учебные материалы и статьи.