предисловие
Mockito
в настоящее время является самым популярныммодульный тест Mock
Рамка. использоватьMock
рамки, мы можемвиртуальныйиз одноговнешние зависимости, нижний тесткомпонентымеждуСвязь, ориентируясь только на кодПроцесс и результаты, чтобы действительно достичь цели тестирования.
текст
Что такое мок
Mock
Китайский перевод — имитация, симуляция, ложь. Для среды тестирования это создание фиктивного/поддельного объекта, чтобы наши тесты могли проходить гладко.
Mock
Тестирование находится в процессе тестирования, для некоторыхне легко построить(какHttpServletRequest
Должен бытьServlet
контейнер можно только построить) или его нелегко получитьдовольно сложныйобъект (например,JDBC
серединаResultSet
объект), используйтевиртуальныйОбъект(Mock
объект) создать для проверки метода.
Зачем использовать пробное тестирование
модульный тестЧтобы проверить правильность нашего кода, мы фокусируемся на процессе кода и правильности результатов.
По сравнению с реальным работающим кодом некоторые из них могут бытьвнешние зависимостиЭтапы конструирования относительно громоздки, и если мы по-прежнему строим внешние зависимости по правилам конструирования реального кода, то работа юнит-тестирования будет сильно увеличена, а код будет перемешан со слишком большим количеством нетестовых частей, а тест случаи будут сложными и трудными для понимания.
использоватьMock
рамки, мы можемвиртуальныйиз одноговнешние зависимости, ориентируясь только на кодПроцесс и результаты, чтобы действительно достичь цели тестирования.
Преимущества фреймворков для пробного тестирования
- Очень просто виртуализировать сложный объект (например, виртуализировать класс реализации интерфейса);
- можно настроить
mock
поведение объекта; - Тестовые примеры можно сделать так, чтобы они фокусировались только на процессе и результатах тестирования;
- Уменьшите связь, которую внешние классы, системы и зависимости привносят в модульное тестирование.
Процесс Мокито
Как показано, используйтеMockito
Общий процесс выглядит следующим образом:
-
Создайтевнешние зависимостииз
Mock
объект, то этоMock
объект, введенный втестовый класссередина; -
воплощать в жизньтестовый код;
-
чектестовый кодПравильно ли он выполнен.
Использование Мокито
существует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
методы:
-
правильно
JUnit
добавлен тестовый класс@RunWith(MockitoJUnitRunner.class)
-
отмечены
@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"));
}
Ограничения Мокито
- не может
mock
статический метод; - не может
mock
конструктор; - не может
mock
equals()
иhashCode()
метод.
Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack
Эта учетная запись будет продолжать делиться сухими товарами серверных технологий, включая основы виртуальных машин, многопоточное программирование, высокопроизводительные фреймворки, асинхронное ПО, промежуточное ПО для кэширования и обмена сообщениями, распределенные и микросервисы, материалы для обучения архитектуре и расширенные учебные материалы и статьи.