Różnica między używaniem MockMvc z SpringBootTest a użyciem WebMvcTest

98

Jestem nowy w Spring Boot i próbuję zrozumieć, jak działa testowanie w SpringBoot. Nie wiem, jaka jest różnica między następującymi dwoma fragmentami kodu:

Fragment kodu 1:

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerApplicationTest {
    @Autowired    
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

Ten test wykorzystuje @WebMvcTestadnotację, która moim zdaniem służy do testowania wycinka funkcji i testuje tylko warstwę MVC aplikacji internetowej.

Fragment kodu 2:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

Ten test używa @SpringBootTestadnotacji i pliku MockMvc. Czym różni się to od fragmentu kodu 1? Co to robi inaczej?

Edycja: dodawanie fragmentu kodu 3 (znaleziono to jako przykład testowania integracji w dokumentacji Spring)

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 
public class HelloControllerIT {
    
    @LocalServerPort private int port;
    private URL base;
    
    @Autowired private TestRestTemplate template;
    
    @Before public void setUp() throws Exception {
        this.base = new URL("http://localhost:" + port + "/");
    }
    
    @Test public void getHello() throws Exception {
        ResponseEntity < String > response = template.getForEntity(base.toString(), String.class);
        assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
    }
}
Revansha
źródło

Odpowiedzi:

89

@SpringBootTestto ogólna adnotacja testu. Jeśli szukasz czegoś, co robi to samo przed wersją 1.4, to właśnie tego powinieneś użyć. W ogóle nie używa dzielenia, co oznacza, że ​​uruchomi pełny kontekst aplikacji i nie będzie w ogóle dostosowywał skanowania komponentów.

@WebMvcTestbędzie skanować tylko zdefiniowany kontroler i infrastrukturę MVC. Otóż ​​to. Więc jeśli twój kontroler jest w pewnym stopniu zależny od innych komponentów bean z warstwy usług, test nie rozpocznie się, dopóki sam nie załadujesz tej konfiguracji, albo nie udostępnisz jej makiety. Jest to znacznie szybsze, ponieważ ładujemy tylko niewielką część Twojej aplikacji. Ta adnotacja używa wycinania.

Przeczytanie tego dokumentu prawdopodobnie również powinno ci pomóc.

Stephane Nicoll
źródło
Wielkie dzięki za odpowiedź !!. Więc jeśli dobrze cię rozumiem, oznacza to, że oba fragmenty kodu testują tylko część aplikacji MVC. Ale fragment kodu 1 ładuje pełny kontekst aplikacji, podczas gdy fragment kodu 2 skanuje tylko kontroler. Czy to jest poprawne? Czy fragment kodu 1 można uznać za test jednostkowy do testowania kontrolera?
Revansha
1
Nie, to nie jest poprawne. SpringBootTestładuje twoją pełną aplikację (do pewnego stopnia, domyślnie nie uruchamia osadzonego kontenera, jeśli jest dostępny, do tego webEnvironmentsłuży). Nie powiedziałbym, że @SpringBootTestjest to test jednostkowy kontrolera, ale raczej test integracji, tak naprawdę. WebMvcTestjest tak naprawdę testem jednostkowym twojego kontrolera w tym sensie, że jeśli ma zależności, będziesz musiał je dostarczyć sam (konfigurację lub jakąś próbę).
Stephane Nicoll
Jeszcze raz dziękuję za odpowiedź. Zmieniłem pytanie i dodałem fragment kodu 3. Wspomniałeś, że adnotacja @SpringBootTest jest częściej używana do testowania integracji. Wierzę, że Snippet 3 to demonstruje. Więc jeśli testy integracyjne są wykonywane tak jak w Snippet 3, to co robi Snippet 2? Snippet 2 używa adnotacji SpringBootTest i symulowanego środowiska (domyślna wartość atrybutu wenEnvironment). Ponadto fragment 3 uruchamia wbudowany serwer i wykonuje naprawdę wywołania HTTP, podczas gdy fragment 2 tego nie robi. Biorąc to pod uwagę, nie można uznać fragmentu 2 za test jednostkowy?
Revansha
4
Nie jestem pewien, czy rozwiążemy to tutaj. Może gitter? Wydaje się, że ciągle tęsknisz za tym, że kontekst aplikacji, który tworzy SpringBootTesti WebMvcTestjest bardzo różny. Pierwsza z nich ładuje CAŁĄ aplikację i włącza WSZYSTKIE automatyczne konfiguracje, podczas gdy druga włącza tylko Spring Mvc i nie skanuje niczego poza HelloController. W końcu wszystko zależy od tego, co masz na myśli przez test jednostkowy. Ale to jest różnica.
Stephane Nicoll
Dzięki za twoją odpowiedź. To dla mnie bardzo pomocne. Teraz rozumiem, dlaczego mój test można uruchomić za pomocą SpringBootTest, ale wyjątek, gdy WebMvcTest. Jeszcze raz wielkie dzięki.
Alpy 1992
71

Adnotacja @SpringBootTest mówi Spring Boot, aby poszedł i poszukał głównej klasy konfiguracji (na przykład z @SpringBootApplication) i użył jej do uruchomienia kontekstu aplikacji Spring. SpringBootTest ładuje całą aplikację i wstrzykuje wszystkie ziarna, które mogą działać wolno.

@WebMvcTest - do testowania warstwy kontrolera i musisz podać pozostałe zależności wymagane przy użyciu Mock Objects.

Kilka dodatkowych adnotacji poniżej w celach informacyjnych.

Testowanie wycinków aplikacji Czasami chcesz przetestować prosty „wycinek” aplikacji zamiast automatycznej konfiguracji całej aplikacji. Spring Boot 1.4 wprowadza 4 nowe adnotacje testowe:

@WebMvcTest - for testing the controller layer
@JsonTest - for testing the JSON marshalling and unmarshalling
@DataJpaTest - for testing the repository layer
@RestClientTests - for testing REST clients

Więcej informacji: https://spring.io/guides/gs/testing-web/

RoshanKumar Mutha
źródło
Oto link do Sping Boot Reference - Test automatycznej konfiguracji adnotacji . Jest więcej niż tylko cztery @ roshankumar-mutha wymienione tutaj. Łącze do przewodnika dla początkujących nie obejmuje szczegółowo tych fragmentów.
George Pantazes
15

Testy MVC mają na celu objęcie tylko kontrolera aplikacji. Żądania i odpowiedzi HTTP są mockowane, więc nie są tworzone rzeczywiste połączenia. Z drugiej strony, kiedy używasz @SpringBootTest, cała konfiguracja kontekstu aplikacji internetowej jest ładowana, a połączenia przechodzą przez prawdziwy serwer sieciowy. W takim przypadku nie używasz MockMvcfasoli, ale RestTemplatezamiast tego standard (lub nowa alternatywa TestRestTemplate).

Kiedy więc powinniśmy wybrać jedną lub drugą? @WebMvcTestma na celu jednostronne przetestowanie kontrolera po stronie serwera. @SpringBootTestz drugiej strony powinien być używany do testów integracyjnych, gdy chcemy współdziałać z aplikacją od strony klienta.

Nie oznacza to, że nie możesz używać makiet z @SpringBootTest; jeśli piszesz test integracji, może to być nadal konieczne. W każdym razie lepiej nie używać go tylko do prostego testu jednostkowego kontrolera.

źródło - Learning Microservices with Spring Boot

Gautam Tadigoppula
źródło
4
Nie rozumiem, dlaczego ta odpowiedź jest pozytywna. Kiedy używasz @SpringBootTest, prawdziwy serwer WWW nie jest uruchamiany, chyba że masz również webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT(lub a DEFINED_PORT), a połączenia nie przechodzą przez prawdziwy serwer WWW. Wartość domyślna @SpringBootTestto WebEnvironment.MOCK.
Koray Tugay