mockito учебник
Начало работы с mockito
Поиск…
замечания
Mockito - это java Mocking framework, целью которого является предоставление возможности писать чистые читаемые модульные тесты, используя простой API. Он отличается от других насмешливых фреймворков, оставив шаблон проверки ожидаемого результата, который использует большинство других фреймворков.
Вместо этого он знает только один способ обмануть (не финальный) классы и интерфейсы и позволяет проверять и заглушать на основе гибких аргументов.
Текущую версию 1.10.19 лучше всего использовать с помощью maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
</dependency>
или град
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
Версия 2 все еще находится в стадии бета-тестирования.
Версии
Версия | Maven Central | Примечания к выпуску | Дата выхода |
---|---|---|---|
2.1.0 | Mockito-жильный | изменения | 2016-10-04 |
1.10.19 | Mockito-жильный | изменения | 2014-12-31 |
Простой модульный тест с использованием Mockito
Класс, который мы собираемся проверить:
public class Service {
private Collaborator collaborator;
public Service(Collaborator collaborator) {
this.collaborator = collaborator;
}
public String performService(String input) {
return collaborator.transformString(input);
}
}
Его соавтор:
public class Collaborator {
public String transformString(String input) {
return doStuff();
}
private String doStuff() {
// This method may be full of bugs
. . .
return someString;
}
}
В нашем тесте мы хотим нарушить зависимость от Collaborator
и его ошибок, поэтому мы собираемся высмеять Collaborator
:
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
public class ServiceTest {
@Test
public void testPerformService() throws Exception {
// Configure mock
Collaborator collaboratorMock = mock(Collaborator.class);
doReturn("output").when(collaboratorMock).transformString("input");
// Perform the test
Service service = new Service(collaboratorMock);
String actual = service.performService("input");
// Junit asserts
String expected = "output";
assertEquals(expected, actual);
}
}
Использование аннотаций Mockito
Класс, который мы собираемся проверить:
public class Service{
private Collaborator collaborator;
public Service(Collaborator collaborator){
this.collaborator = collaborator;
}
public String performService(String input){
return collaborator.transformString(input);
}
}
Его соавтор:
public class Collaborator {
public String transformString(String input){
return doStuff();
}
private String doStuff()
{
// This method may be full of bugs
. . .
return someString;
}
}
В нашем тесте мы хотим нарушить зависимость от Collaborator
и его ошибок, поэтому мы собираемся высмеять Collaborator
. Использование аннотации @Mock
- это удобный способ создания разных экземпляров макетов для каждого теста:
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
@Mock
private Collaborator collaboratorMock;
@InjectMocks
private Service service;
@Test
public void testPerformService() throws Exception {
// Configure mock
doReturn("output").when(collaboratorMock).transformString("input");
// Perform the test
String actual = service.performService("input");
// Junit asserts
String expected = "output";
assertEquals(expected, actual);
}
@Test(expected=Exception.class)
public void testPerformServiceShouldFail() throws Exception {
// Configure mock
doThrow(new Exception()).when(collaboratorMock).transformString("input");
// Perform the test
service.performService("input");
}
}
Mockito попытается разрешить инъекцию зависимостей в следующем порядке:
- Настроек-инжекционные инъекции в конструктор вводятся в конструктор с большинством аргументов (если некоторые аргументы не могут быть найдены, то передаются null). Если объект был успешно создан с помощью конструктора, тогда никакие другие стратегии не будут применены.
- Насыпная инъекция - макеты вводятся по типу. Если есть несколько свойств одного и того же типа, имена свойств и макеты будут сопоставлены.
- Прямая инъекция в поле - такая же, как для инъекции на основе сеттера.
Обратите внимание, что при сбое какой-либо из вышеперечисленных стратегий не сообщается о сбое.
@InjectMocks
получения более подробной информации об этом механизме в последней версии Mockito обратитесь к последнему @InjectMocks.
Установка и настройка
Монтаж
Предпочтительный способ установки Mockito - объявить зависимость от mockito-core
с mockito-core
системой сборки. По состоянию на 22 июля 2016 года последняя не-бета-версия - 1.10.19, но 2.x уже рекомендуется переносить на .
специалист
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
Gradle
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
Существует также mockito-all
что содержит Hamcrest и Objenesis, кроме самого Mockito. Он поставляется через Maven главным образом для пользователей муравьев, но распространение было прекращено в Mockito 2.x.
Импортировать
Большинство объектов Mockito являются статическими методами org.mockito.Mockito
. Таким образом, Mockito может быть статически импортирован в класс таким образом:
import static org.mockito.Mockito.*;
Пункт ввода документации находится в javadoc этого класса.
Вымыть некоторые методы на объекте
Просто некоторые методы объекта можно издеваться с помощью spy()
mockito.
Например, представьте, что для класса метода требуется, чтобы какой-либо веб-сервис работал.
public class UserManager {
List<User> users;
public UserManager() {
user = new LinkedLisk<User>();
}
public void addUser(User user) {
if (isValid(user)) {
user.add(user);
} else {
throw new NotValidUserException();
}
}
protected boolean isValid(User user) {
//some online web service to check if user is valid
}
public int numberOfUsers() {
return users.size();
}
}
Метод addUser
должен быть протестирован, чтобы сделать полезный тест для UserManager
. Однако зависимость найдена здесь, isValid
требует внешней веб-службы, которая не содержится в нашем коде. Затем эту внешнюю зависимость следует нейтрализовать.
В этом случае, если вы только mock isValid
вы сможете протестировать остальные методы UserManager
.
@Test
public void testAddUser() {
User user = mock(User.class);
UserManager manager = spy(new UserManager());
//it forces to manager.isValid to return true
doReturn(true).when(manager).isValid(anyObject());
manager.addUser(user);
assertTrue(manager.numberOfUsers(), 1);
}
Вы можете легко проверить сценарий, когда user
недействителен.
@Test(expectedExceptions = NotValidUserException.class)
public void testNotValidAddUser() {
User user = mock(User.class);
UserManager manager = spy(new UserManager());
//it forces to manager.isValid to return false
doReturn(false).when(manager).isValid(anyObject());
manager.addUser(user);
}
Простой минимальный тест Mockito
В этом примере показан минимальный тест Mockito с использованием издевающегося массива ArrayList
:
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest
{
@Mock
ArrayList<String> listMock;
@Test
public void testAppend() {
// configure the mock to return "foobar" whenever "get()"
// is called on "listMock" with an int value as parameter
doReturn("foobar").when(listMock).get(anyInt());
String result = listMock.get(0);
assertEquals("foobar", result);
}
}
Проверка аргументов с помощью ArgumentCaptor
Чтобы проверить аргументы методов, вызванных макетом, используйте класс ArgumentCaptor
. Это позволит вам извлечь аргументы в свой тестовый метод и выполнить на них утверждения.
В этом примере проверяется метод, который обновляет имя пользователя с заданным ID. Метод загружает пользователя, обновляет атрибут name
с заданным значением и сохраняет его впоследствии. Тест хочет проверить, что аргумент, переданный методу save
является объектом User
с правильным идентификатором и именем.
// This is mocked in the test
interface UserDao {
void save(User user);
}
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@Mock
UserDao userDao;
@Test
public void testSetNameForUser() {
UserService serviceUnderTest = new UserService(userDao);
serviceUnderTest.setNameForUser(1L, "John");
ArgumentCaptor<User> userArgumentCaptor = ArgumentCaptor.forClass(User.class);
verify(userDao).save(userArgumentCaptor.capture());
User savedUser = userArgumentCaptor.getValue();
assertTrue(savedUser.getId() == 1);
assertTrue(savedUser.getName().equals("John"));
}
}
Проверка аргументов с помощью ArgumentMatcher
Mockito предоставляет интерфейс Matcher<T>
вместе с абстрактным классом ArgumentMatcher<T>
для проверки аргументов. Он использует другой подход к одному и тому же варианту использования, чем ArgumentCaptor
. Кроме того, ArgumentMatcher можно использовать и для насмешек. Оба варианта использования используют метод Mockito.argThat()
который обеспечивает приемлемый для чтения тестовый код.
verify(someMock).someMethod(Mockito.argThat(new ArgumentMatcher<String>() {
@Override
public boolean matches(Object o) {
return o instanceof String && !((String)o).isEmpty();
}
});
Из JavaDocs из ArgumentMatcher:
Предупреждение: будьте разумны с использованием сложного сопоставления аргументов, особенно пользовательских совпадений аргументов, поскольку это может сделать тест менее читаемым. Иногда лучше использовать equals () для аргументов, передаваемых в mocks (Mockito естественно использует equals () для сопоставления аргументов). Это может сделать тест чище.
Создавайте объекты, издевавшиеся над Mockito
Существует два способа создания объекта, издевающегося над Mockito:
- через аннотацию
- посредством макетной функции
Через аннотацию:
С помощью тестировщика JUnit:
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock
private Bar barMock;
// ...
}
Вы также можете использовать JUnit @Rule
, который обеспечивает те же функции, что и MockitoJUnitRunner
, но не нуждается в тестировщике @RunWith
:
public class FooTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
private Bar barMock;
// ...
}
Если вы не можете использовать @RunWith
или аннотацию @Rule
вы также можете инициализировать «вручную»:
public class FooTest {
@Mock
private Bar barMock;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
// ...
}
Функция макета:
public class FooTest {
private Bar barMock = Mockito.mock(Bar.class);
// ...
}
Из-за стирания типа вы не можете высмеивать общий класс, как указано выше. Вы должны издеваться над базовым классом и явно вводить его в нужный общий тип:
public class FooTest {
private Bar<String> genericBarMock = (Bar<String>) Mockito.mock(Bar.class);
// ...
}
Добавить поведение к издеваемому объекту
Mockito.when(mock.returnSomething()).thenReturn("my val");
mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "my val" again
mock.returnSomething(); // returns "my val" again and again and again...
Если вы хотите различное значение во втором вызове, вы можете добавить желаемый возвращаемый аргумент в метод thenReturn:
Mockito.when(mock.returnSomething()).thenReturn("my val", "other val");
mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "other val"
mock.returnSomething(); // returns "other val" again
Если вы вызовете метод без добавления поведения в mock, он вернет null:
barMock.mock.returnSomethingElse(); // returns null
В случае, если метод издевательства имеет параметры, вы также должны декларировать значения:
Mockito.when(mock.returnSomething("param 1")).thenReturn("my val 1");
Mockito.when(mock.returnSomething("param 2")).thenReturn("my val 2");
mock.returnSomething("param 1"); // returns "my val 1"
mock.returnSomething("param 2"); // returns "my val 2"
mock.returnSomething("param 3"); // returns null
Если вы не заботитесь о значении параметра, вы можете использовать Matchers.any ():
Mockito.when(mock.returnSomething(Matchers.any())).thenReturn("p1");
mock.returnSomething("param 1"); // returns "p1"
mock.returnSomething("param other"); // returns "p1"
Чтобы использовать исключение, используйте метод thenThrow:
Mockito.when(mock.returnSomething()).thenThrow(new Exception());
mock.returnSomething(); // throws Exception
Проверка аргументов, переданных в mock
Предположим, что у нас есть этот класс, и мы хотели бы протестировать метод doSmth
. В этом случае мы хотим увидеть, передан ли параметр «val» в foo
. Объект foo
издевается.
public class Bar {
private final Foo foo;
public Bar(final Foo foo) {
this.foo = foo;
}
public void doSmth() {
foo.bla("val");
}
}
Мы можем добиться этого с помощью ArgumentCaptor
:
@Mock
private Foo fooMock;
@InjectMocks
private Bar underTest;
@Captor
private ArgumentCaptor<String> stringCaptor;
@Test
public void should_test_smth() {
underTest.doSmth();
Mockito.verify(fooMock).bla(stringCaptor.capture());
assertThat(stringCaptor.getValue(), is("val"));
}
Проверять вызовы метода на издеваемом объекте
Чтобы проверить, был ли Mockito.verify
метод на Mockito.verify
объекте, вы можете использовать метод Mockito.verify
:
Mockito.verify(someMock).bla();
В этом примере мы утверждаем, что метод bla
вызывался на mock-объект someMock
.
Вы также можете проверить, был ли вызван метод с определенными параметрами:
Mockito.verify(someMock).bla("param 1");
Если вы хотите проверить, что метод не был вызван, вы можете передать дополнительный параметр VerificationMode
для verify
:
Mockito.verify(someMock, Mockito.times(0)).bla();
Это также работает, если вы хотите проверить, что этот метод был вызван более одного раза (в этом случае мы проверяем, что метод bla
был вызван 23 раза):
Mockito.verify(someMock, Mockito.times(23)).bla();
Это больше примеров для параметра VerificationMode
, обеспечивающего больший контроль над количеством раз, когда должен вызываться метод:
Mockito.verify(someMock, Mockito.never()).bla(); // same as Mockito.times(0)
Mockito.verify(someMock, Mockito.atLeast(3)).bla(); // min 3 calls
Mockito.verify(someMock, Mockito.atLeastOnce()).bla(); // same as Mockito.atLeast(1)
Mockito.verify(someMock, Mockito.atMost(3)).bla(); // max 3 calls
Пусковые методы
методы void
можно окутать с помощью методов doThrow () , doAnswer () , doNothing () , doCallRealMethod () .
Runnable mock = mock(Runnable.class);
doThrow(new UnsupportedOperationException()).when(mock).run();
mock.run(); // throws the UnsupportedOperationException
Обратите внимание, что методы void
нельзя обрезать, используя when(..)
потому что компилятор не любит методы void
как аргумент.