Разница между @Mock, @MockBean и Mockito.mock ()

147

В чем разница между этими тремя подходами при создании тестов и имитации зависимостей?

  1. @MockBean:

    @MockBean
    MyService myservice;
  2. @Mock:

    @Mock
    MyService myservice;
  3. Mockito.mock ()

    MyService myservice = Mockito.mock(MyService.class);
Doug
источник

Ответы:

200

Простая библиотека Mockito

import org.mockito.Mock;
...
@Mock
MyService myservice;

и

import org.mockito.Mockito;
...
MyService myservice = Mockito.mock(MyService.class);

происходят из библиотеки Mockito и функционально эквивалентны.
Они позволяют имитировать класс или интерфейс, а также записывать и проверять его поведение.

Использование аннотаций короче, поэтому предпочтительнее и часто предпочтительнее.


Обратите внимание, что для включения аннотаций Mockito во время выполнения тестов необходимо вызвать MockitoAnnotations.initMocks(this)статический метод.
Чтобы избежать побочного эффекта между тестами, рекомендуется делать это перед каждым выполнением теста:

@Before 
public void initMocks() {
    MockitoAnnotations.initMocks(this);
}

Другой способ включить аннотации Mockito - это аннотировать тестовый класс @RunWith, указав, MockitoJUnitRunnerкоторый выполняет эту задачу, а также другие полезные вещи:

@RunWith(org.mockito.runners.MockitoJUnitRunner.class)
public MyClassTest{...}

Библиотека Spring Boot, оборачивающая библиотеку Mockito

Это действительно класс Spring Boot :

import org.springframework.boot.test.mock.mockito.MockBean;
...
@MockBean
MyService myservice;

Класс включен в spring-boot-testбиблиотеку.

Это позволяет добавлять mockito mocks в Spring ApplicationContext.
Если в контексте существует bean-компонент, совместимый с объявленным классом, он заменяет его макетом.
Если это не так, он добавляет макет в контекст как bean-компонент.

Ссылка на Javadoc:

Аннотация, которую можно использовать для добавления фиктивных элементов в Spring ApplicationContext.

...

Если любой существующий одиночный компонент того же типа, который определен в контексте, будет заменен макетом, если существующий компонент не определен, будет добавлен новый.


Когда использовать классический / простой Mockito и когда использовать @MockBeanSpring Boot?

Модульные тесты предназначены для тестирования компонента изолированно от других компонентов, и модульные тесты также имеют требование: быть максимально быстрым с точки зрения времени выполнения, поскольку эти тесты могут выполняться каждый день десятки раз на машинах разработчика.

Следовательно, вот простой совет:

Когда вы пишете тест, который не требует каких-либо зависимостей от контейнера Spring Boot, вам подойдет классический / простой Mockito: он быстрый и способствует изоляции тестируемого компонента.
Если ваш тест должен полагаться на контейнер Spring Boot, и вы также хотите добавить или имитировать один из компонентов контейнера: @MockBeanSpring Boot - это выход.


Типичное использование Spring Boot @MockBean

Поскольку мы пишем тестовый класс, аннотированный @WebMvcTest(фрагмент веб-теста).

Документация Spring Boot очень хорошо описывает это:

Часто @WebMvcTestбудет ограничиваться одним контроллером и использоваться в сочетании с @MockBeanпредоставлением имитационных реализаций для необходимых сотрудников.

Вот пример:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(FooController.class)
public class FooControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private FooService fooServiceMock;

    @Test
    public void testExample() throws Exception {
         Foo mockedFoo = new Foo("one", "two");

         Mockito.when(fooServiceMock.get(1))
                .thenReturn(mockedFoo);

         mvc.perform(get("foos/1")
            .accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andExpect(content().string("one two"));
    }

}
davidxxx
источник
4
Будет ли использование @MockBean создавать копию bean-компонента и вставлять ее в ApplicationContext? Или все методы фиктивного bean-компонента будут нулевыми? Если все методы равны нулю, могу ли я заглушить их, как я могу использовать @Mock?
Дуг
6
Как объяснялось, использование @MockBeanзаменит bean-компонент в контексте приложения, если bean-компонент, объявляющий тот же тип, уже определен в вашей конфигурации Spring. И инъекция выполняется в классе, в котором вы объявляете. @MockBean.Механизмы DI работают следующим образом: вы регистрируете объект в контексте DI, а затем можете внедрить объект, на который имеется ссылка в контексте Spring, в конкретный класс. Вы не внедряете объект в контекст DI.
davidxxx
13

В конце концов, это легко объяснить. Если вы просто заглянете в javadocs аннотаций, вы увидите разницу:

@Mock: ( org.mockito.Mock)

Отметьте поле как фиктивное.

  • Позволяет создавать сокращенные макеты.
  • Минимизирует повторяющийся код создания макета.
  • Делает тестовый класс более читабельным.
  • Облегчает чтение ошибки проверки, поскольку имя поля используется для идентификации фиктивного объекта.

@MockBean: ( org.springframework.boot.test.mock.mockito.MockBean)

Аннотация, которую можно использовать для добавления фиктивных элементов в Spring ApplicationContext. Может использоваться как аннотация на уровне класса или для полей в @Configurationклассах или тестовых классах, которые являются @RunWithSpringRunner.

Моки могут быть зарегистрированы по типу или по имени компонента. Любой существующий одиночный bean-компонент того же типа, определенный в контексте, будет заменен имитацией, если существующий bean-компонент не определен, будет добавлен новый.

Когда @MockBeanиспользуется в поле, а также зарегистрирован в контексте приложения, макет также будет введен в поле.

Mockito.mock ()

Это просто представление файла @Mock.

Патрик
источник
5
Не будем забывать, что @Mock требует, чтобы MockitoRunner или initMocks вызывались вручную.
Флориан Шетц
4
Единственная разница между @MockBeanи в @Mockтом, что один будет вводить макет в, Spring ApplicationContextа другой - нет?
Дуг
3
@Doug Вы хорошо сформулировали это, но нужно помнить, что MockBean является частью Spring Boot
комивент