mockito Handledning
Komma igång med mockito
Sök…
Anmärkningar
Mockito är ett java Mocking-ramverk som syftar till att tillhandahålla förmågan att skriva rent en läsbar enhetstest med hjälp av det enkla API. Det skiljer sig från andra hålande ramverk genom att lämna det förväntade körningsverifieringsmönstret som de flesta andra ramverk använder.
Istället vet det bara ett sätt att håna (icke-slutliga) klasser och gränssnitt och tillåter att verifiera och stoppa baserat på flexibla argumentmatchare.
Den nuvarande version 1.10.19 erhålls bäst med maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
</dependency>
eller gradle
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
Version 2 finns fortfarande i beta.
versioner
Version | Maven Central | Släppanteckningar | Utgivningsdatum |
---|---|---|---|
2.1.0 | Mockito-core | ändringar | 2016/10/04 |
1.10.19 | Mockito-core | ändringar | 2014/12/31 |
Enkelt enhetstest med Mockito
Klassen vi ska testa är:
public class Service {
private Collaborator collaborator;
public Service(Collaborator collaborator) {
this.collaborator = collaborator;
}
public String performService(String input) {
return collaborator.transformString(input);
}
}
Dess samarbetspartner är:
public class Collaborator {
public String transformString(String input) {
return doStuff();
}
private String doStuff() {
// This method may be full of bugs
. . .
return someString;
}
}
I vårt test vill vi bryta beroendet från Collaborator
och dess buggar, så vi kommer att håna 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);
}
}
Använda Mockito-kommentarer
Klassen vi ska testa är:
public class Service{
private Collaborator collaborator;
public Service(Collaborator collaborator){
this.collaborator = collaborator;
}
public String performService(String input){
return collaborator.transformString(input);
}
}
Dess samarbetspartner är:
public class Collaborator {
public String transformString(String input){
return doStuff();
}
private String doStuff()
{
// This method may be full of bugs
. . .
return someString;
}
}
I vårt test vill vi bryta beroendet från Collaborator
och dess buggar, så vi kommer att håna Collaborator
. Att använda @Mock
kommentarer är ett bekvämt sätt att skapa olika instanser av håravfall för varje test:
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 försöker lösa beroendeinjektion i följande ordning:
- Konstruktorbaserad injektion - hårav sprutas in i konstruktören med de flesta argument (om vissa argument inte kan hittas, då passas nollor). Om ett objekt skapades med framgång via konstruktör kommer inga andra strategier att tillämpas.
- Seterbaserad injektion - håravfall injiceras efter typ. Om det finns flera egenskaper av samma typ, kommer egenskapens namn och hånappningar att matchas.
- Direktfältinsprutning - samma som för setterbaserad injektion.
Observera att inget fel rapporteras i fallet om någon av de nämnda strategierna misslyckades.
Se de senaste @InjectMocks
för mer detaljerad information om denna mekanism i den senaste versionen av Mockito.
Installation och installation
Installation
Det föredragna sättet att installera Mockito är att förklara ett beroende av mockito-core
med ett valfritt byggsystem. Från och med 22 juli 2016 är den senaste versionen som inte är beta-version 1.10.19, men 2.x uppmuntras redan att migreras till .
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.+" }
Det finns också mockito-all
som innehåller Hamcrest och Objenesis förutom Mockito själv. Det levereras via Maven huvudsakligen för myranvändare, men distributionen har avbrutits i Mockito 2.x.
Importera
De flesta av Mockito-anläggningarna är statiska metoder för org.mockito.Mockito
. Således kan Mockito statiskt importeras till en klass på detta sätt:
import static org.mockito.Mockito.*;
Dokumentationspost finns i javadoc för denna klass.
Hånga vissa metoder på ett objekt
Bara vissa metoder för ett objekt kan hånas med spy()
av mockito.
Föreställ dig till exempel att metodklassen kräver viss webbtjänst för att fungera.
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
metoden måste testas för att göra ett användbart test för UserManager
. Men ett beroende finns här, isValid
kräver en extern webbtjänst som inte finns i vår kod. Därefter bör detta yttre beroende neutraliseras.
Om du bara hånar isValid
du testa resten av UserManager
metoderna.
@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);
}
Du kan enkelt kontrollera scenariot där user
inte är giltig.
@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);
}
Enkelt minimalt Mockito-test
Detta exempel visar ett minimalt Mockito-test med en hånad 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);
}
}
Verifiera argument med ArgumentCaptor
ArgumentCaptor
klassen ArgumentCaptor
att validera argument till metoder som kallas för en håna. Detta gör att du kan extrahera argumenten i din testmetod och utföra påståenden om dem.
Detta exempel testar en metod som uppdaterar namnet på en användare med ett givet ID. Metoden laddar användaren uppdaterar name
attribut med det givna värdet och sparar det efteråt. Testet vill kontrollera att argumentet skickas till save
metoden är en User
med rätt ID och namn.
// 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"));
}
}
Verifiera argument med ArgumentMatcher
Mockito tillhandahåller ett Matcher<T>
gränssnitt tillsammans med en abstrakt ArgumentMatcher<T>
klass för att verifiera argument. Den använder en annan metod för samma användningsfall än ArgumentCaptor
. Dessutom kan ArgumentMatcher också användas för att håna. Båda användningsfallen använder sig av Mockito.argThat()
-metoden som ger en rimligt läsbar testkod.
verify(someMock).someMethod(Mockito.argThat(new ArgumentMatcher<String>() {
@Override
public boolean matches(Object o) {
return o instanceof String && !((String)o).isEmpty();
}
});
Från JavaDocs of ArgumentMatcher:
Varning: Var rimlig med att använda komplicerade argumentmatchningar, särskilt anpassade argumentmatare, eftersom det kan göra testet mindre läsbart. Ibland är det bättre att implementera lika () för argument som skickas till mock (Mockito använder naturligtvis lika () för argumentmatchning). Detta kan göra testet renare.
Skapa objekt hånade av Mockito
Det finns två sätt att skapa objekt som hånats av Mockito:
- via annotering
- via mock-funktion
Via en kommentar:
Med en JUnit-testlöpare:
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock
private Bar barMock;
// ...
}
Du kan också använda Mockitos JUnit @Rule
, som ger samma funktionalitet som MockitoJUnitRunner
, men behöver inte en @RunWith
:
public class FooTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
private Bar barMock;
// ...
}
Om du inte kan använda @RunWith
eller @Rule
anteckningen kan du också initta skott "per hand":
public class FooTest {
@Mock
private Bar barMock;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
// ...
}
Via mock-funktion:
public class FooTest {
private Bar barMock = Mockito.mock(Bar.class);
// ...
}
På grund av radering av typen kan du inte håna en generisk klass som ovan. Du måste håna basklassen och uttryckligen kasta till rätt generisk typ:
public class FooTest {
private Bar<String> genericBarMock = (Bar<String>) Mockito.mock(Bar.class);
// ...
}
Lägg till beteende till det hånade objektet
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...
Om du vill ha annat värde på det andra samtalet kan du lägga till önskat returargument till thenReturn-metoden:
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
Om du kommer att ringa metoden utan att lägga till beteende till håna kommer den att returnera noll:
barMock.mock.returnSomethingElse(); // returns null
Om den hånade metoden har parametrar bör du också ange värden:
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
Om du inte bryr dig om paramvärdet kan du använda Matchers.any ():
Mockito.when(mock.returnSomething(Matchers.any())).thenReturn("p1");
mock.returnSomething("param 1"); // returns "p1"
mock.returnSomething("param other"); // returns "p1"
För att kasta undantag använder du sedanTrowrow-metoden:
Mockito.when(mock.returnSomething()).thenThrow(new Exception());
mock.returnSomething(); // throws Exception
Kontrollera argument som skickats till håna
Låt oss anta att vi har den här klassen och vi vill testa doSmth
metoden. I det här fallet vill vi se om parameter "val" skickas till foo
. Objekt foo
hånas.
public class Bar {
private final Foo foo;
public Bar(final Foo foo) {
this.foo = foo;
}
public void doSmth() {
foo.bla("val");
}
}
Vi kan uppnå detta med 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"));
}
Verifiera metodsamtal på hånligt objekt
För att kontrollera om en metod kallades på ett hånat objekt kan du använda metoden Mockito.verify
:
Mockito.verify(someMock).bla();
I det här exemplet hävdar vi att metoden bla
kallades på someMock
.
Du kan också kontrollera om en metod har anropats med vissa parametrar:
Mockito.verify(someMock).bla("param 1");
Om du vill kontrollera att en metod inte kallades kan du skicka en ytterligare VerificationMode
parameter för att verify
:
Mockito.verify(someMock, Mockito.times(0)).bla();
Detta fungerar också om du vill kontrollera att den här metoden kallades mer än en gång (i detta fall kontrollerar vi att metoden bla
heter 23 gånger):
Mockito.verify(someMock, Mockito.times(23)).bla();
Dessa är fler exempel på VerificationMode
parametern, vilket ger mer kontroll över antalet gånger en metod bör kallas:
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
Stubbing void metoder
void
kan stoppas med hjälp av doThrow () , doAnswer () , doNothing () , doCallRealMethod () -familjens metoder.
Runnable mock = mock(Runnable.class);
doThrow(new UnsupportedOperationException()).when(mock).run();
mock.run(); // throws the UnsupportedOperationException
Observera att void
metoder inte kan stoppas med when(..)
orsakar att kompilatorn inte gillar void
som argument.