Różnica między @Mock, @MockBean i Mockito.mock ()

165

Podczas tworzenia testów i mockowania zależności, jaka jest różnica między tymi trzema podejściami?

  1. @MockBean:

    @MockBean
    MyService myservice;
    
  2. @Drwić:

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

    MyService myservice = Mockito.mock(MyService.class);
    
Doug
źródło

Odpowiedzi:

223

Zwykła biblioteka Mockito

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

i

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

pochodzą z biblioteki Mockito i są funkcjonalnie równoważne.
Pozwalają na mockowanie klasy lub interfejsu oraz rejestrowanie i weryfikację zachowań na nim.

Sposób korzystania z adnotacji jest krótszy, dlatego jest preferowany i często preferowany.


Należy pamiętać, że aby włączyć adnotacje Mockito podczas wykonywania testów, MockitoAnnotations.initMocks(this)należy wywołać metodę statyczną.
Aby uniknąć skutków ubocznych między testami, zaleca się robić to przed każdym wykonaniem testu:

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

Innym sposobem włączenia adnotacji Mockito jest dodanie adnotacji do klasy testowej @RunWithpoprzez określenie, MockitoJUnitRunnerktóra wykonuje to zadanie, a także inne przydatne rzeczy:

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

Biblioteka Spring Boot opakowująca bibliotekę Mockito

To jest rzeczywiście klasa Spring Boot :

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

Klasa jest zawarta w spring-boot-testbibliotece.

Pozwala na dodanie Mockito na wiosnę ApplicationContext.
Jeśli bean, zgodny z zadeklarowaną klasą, istnieje w kontekście, zastępuje go makietą.
Jeśli tak nie jest, dodaje makietę w kontekście jako ziarno.

Dokumentacja Javadoc:

Adnotacja, której można użyć do dodania mocków do Spring ApplicationContext.

...

Jeśli jakakolwiek istniejąca pojedyncza fasola tego samego typu zdefiniowana w kontekście zostanie zastąpiona makietą, jeśli żadna istniejąca fasola nie zostanie zdefiniowana, zostanie dodana nowa.


Kiedy używasz klasycznego / zwykłego Mockito i kiedy używasz @MockBeanSpring Boot?

Testy jednostkowe są przeznaczone do testowania komponentu w oderwaniu od innych komponentów, a testy jednostkowe mają również wymóg: być możliwie najszybszym pod względem czasu wykonania, ponieważ testy te mogą być wykonywane każdego dnia kilkadziesiąt razy na maszynach deweloperskich.

W związku z tym oto prosta wskazówka:

Kiedy piszesz test, który nie potrzebuje żadnych zależności z kontenera Spring Boot, klasyczny / prosty Mockito jest drogą do naśladowania: jest szybki i sprzyja izolacji testowanego komponentu.
Jeśli Twój test musi opierać się na kontenerze Spring Boot i chcesz również dodać lub udawać jeden z komponentów bean pojemnika: @MockBeanSpring Boot jest najlepszym rozwiązaniem.


Typowe zastosowanie Spring Boot @MockBean

Podczas pisania klasy testowej z adnotacją @WebMvcTest(wycinek testu internetowego).

Dokumentacja Spring Boot bardzo dobrze to podsumowuje:

Często @WebMvcTestbędzie ograniczony do jednego kontrolera i używany w połączeniu z @MockBeansymulowanymi implementacjami dla wymaganych współpracowników.

Oto przykład :

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
źródło
4
Czy użycie @MockBean utworzy kopię fasoli i wstrzyknie ją do ApplicationContext? A może fasola będzie miała wszystkie swoje metody jako zerowe? Jeśli wszystkie metody są zerowe, czy mogę je odgiąć, tak jak mogę to zrobić za pomocą @Mock?
Doug
6
Jak wyjaśniono, użycie @MockBeanspowoduje zastąpienie komponentu bean w kontekście aplikacji, jeśli komponent bean deklarujący ten sam typ jest już zdefiniowany w konfiguracji Spring. I wstrzyknięcie jest wykonywane w klasie, w której deklarujesz @MockBean.. Mechanizmy DI działają w ten sposób: rejestrujesz obiekt w kontekście DI, a następnie możesz wstrzyknąć obiekt, do którego odwołuje się kontekst Spring, w określonej klasie. Nie wstrzykujesz obiektu w kontekście DI.
davidxxx
13

Na koniec łatwo to wytłumaczyć. Jeśli spojrzysz tylko na javadoc adnotacji, zobaczysz różnice:

@Mock: ( org.mockito.Mock)

Oznacz pole jako próbę.

  • Pozwala na skróconą symulację tworzenia.
  • Minimalizuje powtarzający się kod tworzenia makiety.
  • Sprawia, że ​​klasa testowa jest bardziej czytelna.
  • Sprawia, że ​​błąd weryfikacji jest łatwiejszy do odczytania, ponieważ nazwa pola służy do identyfikacji makiety.

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

Adnotacja, której można użyć do dodania mocków do Spring ApplicationContext. Może być używany jako adnotacja na poziomie klasy lub w polach w @Configurationklasach lub klasach testowych, które są @RunWithSpringRunner.

Mocks można zarejestrować według typu lub nazwy fasoli. Wszelkie istniejące pojedyncze ziarna tego samego typu zdefiniowane w kontekście zostaną zastąpione makietą, jeśli nie zdefiniowano żadnego z nich, zostanie dodany nowy.

Gdy @MockBeanjest używany na polu, a także jest rejestrowany w kontekście aplikacji, makieta zostanie również wstrzyknięta do pola.

Mockito.mock ()

To tylko reprezentacja pliku @Mock.

Patrick
źródło
5
Nie zapominajmy, że @Mock wymaga ręcznego wywołania MockitoRunner lub initMocks.
Florian Schaetz
4
Czy jedyna różnica między @MockBeani to, @Mockże jeden wstrzyknie próbę do Spring ApplicationContextdrugiego, a drugi nie?
Doug
3
@Doug Dobrze to podsumowałeś, ale trzeba pamiętać, że MockBean jest częścią Spring Boot
comiventor
Aby użyć @MockBean, musisz dodać adnotację do klasy @RunWith(SpringRunner.class). Jednak do używania @Mockmożesz użyć @RunWith(MockitoJUnitRunner.class)i wywołać initMocks (), jak wspomniał @ Florian-schaetz. @Mockbędzie działać również ze SpringRunner, ale z dodatkowym obciążeniem związanym z ładowaniem aplikacjiKontekst
Jaison Varghese