mockito Samouczek
Rozpoczęcie pracy z mockito
Szukaj…
Uwagi
Mockito to frameworki Jock, które mają na celu zapewnienie możliwości pisania czystych, czytelnych testów jednostkowych przy użyciu prostego interfejsu API. Różni się od innych fałszywych frameworków tym, że pozostawia wzorzec „oczekuj-uruchom-zweryfikuj”, z którego korzysta większość innych frameworków.
Zamiast tego zna tylko jeden sposób na kpowanie (nie-końcowe) klasy i interfejsy oraz pozwala na weryfikację i odgałęzienie na podstawie elastycznych dopasowań argumentów.
Obecną wersję 1.10.19 najlepiej uzyskać przy pomocy maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
</dependency>
lub stopień
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
Wersja 2 jest wciąż w fazie beta.
Wersje
Wersja | Maven Central | Informacje o wydaniu | Data wydania |
---|---|---|---|
2.1.0 | mockito-core | zmiany | 04.10.2016 |
1.10.19 | mockito-core | zmiany | 31.12.2014 |
Prosty test jednostkowy przy użyciu Mockito
Klasa, którą będziemy testować to:
public class Service {
private Collaborator collaborator;
public Service(Collaborator collaborator) {
this.collaborator = collaborator;
}
public String performService(String input) {
return collaborator.transformString(input);
}
}
Jego współpracownikiem jest:
public class Collaborator {
public String transformString(String input) {
return doStuff();
}
private String doStuff() {
// This method may be full of bugs
. . .
return someString;
}
}
W naszym teście chcemy przełamać zależność od Collaborator
i jego błędów, więc zamierzamy wyśmiewać 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);
}
}
Korzystanie z adnotacji Mockito
Klasa, którą będziemy testować to:
public class Service{
private Collaborator collaborator;
public Service(Collaborator collaborator){
this.collaborator = collaborator;
}
public String performService(String input){
return collaborator.transformString(input);
}
}
Jego współpracownikiem jest:
public class Collaborator {
public String transformString(String input){
return doStuff();
}
private String doStuff()
{
// This method may be full of bugs
. . .
return someString;
}
}
W naszym teście chcemy przełamać zależność od Collaborator
i jego błędów, więc zamierzamy kpić z Collaborator
. Korzystanie z adnotacji @Mock
jest wygodnym sposobem tworzenia różnych instancji prób dla każdego testu:
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 spróbuje rozwiązać wstrzyknięcie zależności w następującej kolejności:
- Zastrzyk oparty na konstruktorze - do konstruktora wstrzykuje się większość argumentów (jeśli nie można znaleźć niektórych argumentów, przekazywane są wartości null). Jeśli obiekt został pomyślnie utworzony za pomocą konstruktora, nie zostaną zastosowane żadne inne strategie.
- Zastrzyk na bazie setera - próbki są wstrzykiwane według rodzaju. Jeśli istnieje kilka właściwości tego samego typu, zostaną dopasowane nazwy właściwości i imiona.
- Bezpośredni wtrysk w polu - taki sam jak w przypadku wtrysku na bazie setera.
Należy pamiętać, że awaria nie jest zgłaszana w przypadku niepowodzenia którejkolwiek z wyżej wymienionych strategii.
Proszę zapoznać się z najnowszymi @InjectMocks
Bardziej szczegółowe informacje na temat tego mechanizmu w najnowszej wersji Mockito.
Instalacja i konfiguracja
Instalacja
Preferowanym sposobem instalacji Mockito jest zadeklarowanie zależności od mockito-core
z wybranym systemem kompilacji. Według stanu na 22 lipca 2016 r. Najnowszą wersją inną niż beta jest wersja 1.10.19, ale migracja do wersji 2.x jest już zalecana.
Maven
<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.+" }
Istnieje również mockito-all
które zawiera Hamcrest i Objenesis oprócz samego Mockito. Jest dostarczany przez Maven głównie dla użytkowników mrówek, ale dystrybucja została przerwana w Mockito 2.x.
Import
Większość funkcji Mockito to statyczne metody org.mockito.Mockito
. W ten sposób Mockito można statycznie zaimportować do klasy w następujący sposób:
import static org.mockito.Mockito.*;
Punkt wejścia dokumentacji znajduje się w javadoc tej klasy.
Wyśmiewać niektóre metody z obiektu
Tylko niektóre metody obiektu można wyśmiewać za pomocą spy()
z mockito.
Wyobraź sobie na przykład, że klasa metod wymaga pewnych usług sieciowych do działania.
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();
}
}
Metoda addUser
musi zostać przetestowana, aby wykonać użyteczny test dla UserManager
. Znajduje się tu jednak zależność, isValid
wymaga zewnętrznej usługi internetowej, która nie jest zawarta w naszym kodzie. Następnie tę zewnętrzną zależność należy zneutralizować.
W takim przypadku, jeśli tylko isValid
, będziesz mógł przetestować pozostałe metody 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);
}
Możesz łatwo sprawdzić scenariusz, w którym user
jest nieprawidłowy.
@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);
}
Prosty minimalny test Mockito
Ten przykład pokazuje minimalny test Mockito przy użyciu fałszywej 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);
}
}
Weryfikacja argumentów za pomocą ArgumentCaptor
Aby sprawdzić poprawność argumentów w metodach wywoływanych na próbce, użyj klasy ArgumentCaptor
. Umożliwi to wyodrębnienie argumentów do metody testowej i wykonanie na nich asercji.
W tym przykładzie przetestowano metodę, która aktualizuje nazwę użytkownika o danym identyfikatorze. Metoda ładuje użytkownika, aktualizuje atrybut name
podaną wartością i zapisuje go później. Test chce upewnić się, że argument przekazany do save
metody jest User
obiekt z prawidłowym ID i nazwę.
// 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"));
}
}
Weryfikacja argumentów za pomocą ArgumentMatcher
Mockito zapewnia interfejs Matcher<T>
wraz z abstrakcyjną klasą ArgumentMatcher<T>
do weryfikacji argumentów. Używa innego podejścia do tego samego przypadku użycia niż ArgumentCaptor
. Dodatkowo ArgumentMatcher może być również używany w kpinach. Oba przypadki użycia wykorzystują metodę Mockito.argThat()
, która zapewnia czytelny kod testowy.
verify(someMock).someMethod(Mockito.argThat(new ArgumentMatcher<String>() {
@Override
public boolean matches(Object o) {
return o instanceof String && !((String)o).isEmpty();
}
});
Z JavaDocs ArgumentMatcher:
Ostrzeżenie: bądź rozsądny, używając skomplikowanego dopasowywania argumentów, zwłaszcza niestandardowych dopasowań argumentów, ponieważ może to spowodować, że test będzie mniej czytelny. Czasami lepiej jest zaimplementować equals () dla argumentów przekazywanych do prób (Mockito naturalnie używa equals () do dopasowywania argumentów). To może uczynić test czystszym.
Twórz obiekty wyśmiewane przez Mockito
Istnieją dwa sposoby tworzenia obiektu wyśmiewanego przez Mockito:
- za pomocą adnotacji
- poprzez funkcję makiety
Za pomocą adnotacji:
Z programem testującym JUnit:
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock
private Bar barMock;
// ...
}
Możesz także użyć JUnit @Rule
, który zapewnia taką samą funkcjonalność jak MockitoJUnitRunner
, ale nie wymaga @RunWith
testera:
public class FooTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
private Bar barMock;
// ...
}
Jeśli nie można użyć @RunWith
lub @Rule
adnotacji można również init mocks „za rękę”:
public class FooTest {
@Mock
private Bar barMock;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
// ...
}
Poprzez funkcję makiety:
public class FooTest {
private Bar barMock = Mockito.mock(Bar.class);
// ...
}
Z powodu wymazywania typu nie można kpić z ogólnej klasy, jak wyżej. Musisz wyśmiewać klasę podstawową i jawnie rzutować na odpowiedni typ ogólny:
public class FooTest {
private Bar<String> genericBarMock = (Bar<String>) Mockito.mock(Bar.class);
// ...
}
Dodaj zachowanie do wyśmiewanego obiektu
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...
Jeśli chcesz mieć inną wartość przy drugim wywołaniu, możesz dodać żądany argument powrotu do metody 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
Jeśli wywołasz metodę bez dodawania zachowania do próby, zwróci null:
barMock.mock.returnSomethingElse(); // returns null
W przypadku, gdy próbowana metoda ma parametry, należy również zadeklarować wartości:
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
Jeśli nie zależy ci na wartości parametru, możesz użyć Matchers.any ():
Mockito.when(mock.returnSomething(Matchers.any())).thenReturn("p1");
mock.returnSomething("param 1"); // returns "p1"
mock.returnSomething("param other"); // returns "p1"
Aby zgłosić wyjątek, użyj metody thenThrow:
Mockito.when(mock.returnSomething()).thenThrow(new Exception());
mock.returnSomething(); // throws Exception
Sprawdź argumenty przekazane próbnemu
Załóżmy, że mamy tę klasę i chcielibyśmy przetestować metodę doSmth
. W tym przypadku chcemy sprawdzić, czy parametr „val” jest przekazywany do foo
. Obiekt foo
jest wyśmiewany.
public class Bar {
private final Foo foo;
public Bar(final Foo foo) {
this.foo = foo;
}
public void doSmth() {
foo.bla("val");
}
}
Możemy to osiągnąć za pomocą 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"));
}
Sprawdź wywołania metod na wyśmiewanym obiekcie
Aby sprawdzić, czy metoda została wywołana na Mockito.verify
obiekcie, możesz użyć metody Mockito.verify
:
Mockito.verify(someMock).bla();
W tym przykładzie twierdzimy, że metoda bla
została wywołana na obiekcie someMock
.
Możesz także sprawdzić, czy metoda została wywołana z określonymi parametrami:
Mockito.verify(someMock).bla("param 1");
Jeśli chcesz sprawdzić, czy metoda nie została wywołana, możesz przekazać dodatkowy parametr VerificationMode
, aby verify
:
Mockito.verify(someMock, Mockito.times(0)).bla();
Działa to również, jeśli chcesz sprawdzić, czy ta metoda została wywołana więcej niż jeden raz (w tym przypadku sprawdzamy, że metoda bla
została wywołana 23 razy):
Mockito.verify(someMock, Mockito.times(23)).bla();
Oto więcej przykładów parametru VerificationMode
, zapewniających większą kontrolę nad liczbą wywołań metody:
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
Stubbingowe metody pustki
Metody void
można stubować przy użyciu metod z rodziny doThrow () , doAnswer () , doNothing () , doCallRealMethod () .
Runnable mock = mock(Runnable.class);
doThrow(new UnsupportedOperationException()).when(mock).run();
mock.run(); // throws the UnsupportedOperationException
Zauważ, że nie można void
metod void
when(..)
powoduje, że kompilator nie lubi metod void
jako argumentu.