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.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

Существует два способа создания объекта, издевающегося над 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 - объявить зависимость от 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);
    }
}
 

Простой модульный тест с использованием 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);
    }  
}
 

Пусковые методы

методы void можно окутать с помощью методов doThrow () , doAnswer () , doNothing () , doCallRealMethod () .

Runnable mock = mock(Runnable.class);

doThrow(new UnsupportedOperationException()).when(mock).run();

mock.run(); // throws the UnsupportedOperationException
 

Обратите внимание, что методы void нельзя обрезать, используя when(..) потому что компилятор не любит методы void как аргумент.

Использование аннотаций 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 попытается разрешить инъекцию зависимостей в следующем порядке:

  1. Настроек-инжекционные инъекции в конструктор вводятся в конструктор с большинством аргументов (если некоторые аргументы не могут быть найдены, то передаются null). Если объект был успешно создан с помощью конструктора, тогда никакие другие стратегии не будут применены.
  2. Насыпная инъекция - макеты вводятся по типу. Если есть несколько свойств одного и того же типа, имена свойств и макеты будут сопоставлены.
  3. Прямая инъекция в поле - такая же, как для инъекции на основе сеттера.

Обратите внимание, что при сбое какой-либо из вышеперечисленных стратегий не сообщается о сбое.

@InjectMocks получения более подробной информации об этом механизме в последней версии Mockito обратитесь к последнему @InjectMocks.

Проверять вызовы метода на издеваемом объекте

Чтобы проверить, был ли 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
 

Проверка аргументов с помощью 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 () для сопоставления аргументов). Это может сделать тест чище.