Разница между использованием MockMvc с SpringBootTest и использованием WebMvcTest

96

Я новичок в Spring Boot и пытаюсь понять, как работает тестирование в SpringBoot. Я немного смущен тем, в чем разница между следующими двумя фрагментами кода:

Фрагмент кода 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!")));
    }
}

В этом тесте используется @WebMvcTestаннотация, которая, как я полагаю, предназначена для тестирования фрагментов функций, и проверяется только уровень MVC веб-приложения.

Фрагмент кода 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!")));
    }
}

Этот тест использует @SpringBootTestаннотацию и файл MockMvc. Так чем же он отличается от фрагмента кода 1? Что это делает по-другому?

Изменить: добавление фрагмента кода 3 (нашел это как пример интеграционного тестирования в документации 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!"));
    }
}
Реванша
источник

Ответы:

88

@SpringBootTestэто общая аннотация теста. Если вы ищете что-то, что делает то же самое до 1.4, вам следует использовать именно его. Он вообще не использует нарезку, что означает, что он запускает полный контекст вашего приложения и вообще не настраивает сканирование компонентов.

@WebMvcTestбудет сканировать только определенный вами контроллер и инфраструктуру MVC. Вот и все. Поэтому, если ваш контроллер имеет некоторую зависимость от других bean-компонентов на уровне обслуживания, тест не начнется, пока вы сами не загрузите эту конфигурацию или не предоставите для нее макет. Это намного быстрее, поскольку мы загружаем лишь небольшую часть вашего приложения. В этой аннотации используется нарезка.

Возможно, вам также поможет чтение документа .

Стефан Николл
источник
Большое спасибо за ответ !!. Итак, если я правильно вас понял, это означает, что оба фрагмента кода тестируют только часть MVC приложения. Но фрагмент кода 1 загружает весь контекст приложения, тогда как фрагмент кода 2 сканирует только контроллер. Это правильно? Можно ли рассматривать фрагмент кода 1 как модульный тест для тестирования контроллера?
Реванша 06
1
Нет, это не так. SpringBootTestзагружает ваше полное приложение (в некоторой степени, по умолчанию он не запускает встроенный контейнер, если он доступен, для чего webEnvironmentон нужен). Я бы не сказал, что @SpringBootTestэто юнит-тест контроллера, это скорее интеграционный тест. WebMvcTestна самом деле является модульным тестом вашего контроллера в том смысле, что если у него есть зависимость, вам придется предоставить их самостоятельно (либо конфигурацию, либо какой-то макет).
Стефан Николл
Еще раз спасибо за ответ. Я отредактировал вопрос и добавил фрагмент кода 3. Вы упомянули, что аннотация @SpringBootTest больше используется для интеграционного тестирования. Я считаю, что фрагмент 3 демонстрирует это. Итак, если интеграционное тестирование выполняется, как в фрагменте 3, то что делает фрагмент 2? Фрагмент 2 использует аннотацию SpringBootTest и фиктивную среду (значение по умолчанию для атрибута wenEnvironment). Кроме того, фрагмент 3 запускает встроенный сервер и действительно выполняет HTTP-вызовы, тогда как фрагмент 2 этого не делает. Итак, учитывая это, нельзя ли считать фрагмент 2 модульным тестом?
Реванша 06
4
Я не уверен, что мы собираемся здесь разбираться. Может быть, gitter? Кажется, вы постоянно упускаете из виду то, что контекст приложения, который создается SpringBootTestи WebMvcTestсоздается, сильно различается. Первый загружает ваше ВСЕ приложение и включает ВСЕ автоконфигурации, а второй включает только Spring Mvc и не сканирует ничего, кроме HelloController. В конце концов, все зависит от того, что вы подразумеваете под модульным тестом. Но вот в чем разница.
Стефан Николл
Спасибо за ваш ответ. Это очень помогло мне. Теперь я понимаю, почему мой тест может работать с SpringBootTest, но исключение, когда WebMvcTest. Еще раз большое спасибо.
Alps1992,
69

Аннотация @SpringBootTest сообщает Spring Boot, что нужно искать основной класс конфигурации (например, с @SpringBootApplication) и использовать его для запуска контекста приложения Spring. SpringBootTest загружает полное приложение и внедряет все bean-компоненты, которые могут быть медленными.

@WebMvcTest - для тестирования уровня контроллера, и вам необходимо предоставить оставшиеся зависимости, необходимые для использования Mock Objects.

Еще несколько аннотаций ниже для справки.

Тестирование фрагментов приложения Иногда вы хотите протестировать простой «фрагмент» приложения вместо автоматической настройки всего приложения. Spring Boot 1.4 представляет 4 новых тестовых аннотации:

@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

См. Дополнительную информацию: https://spring.io/guides/gs/testing-web/

РошанКумар Мута
источник
Вот ссылка на Sping Boot Reference - Test Auto-configuration Annotations . Здесь не только четыре @ roshankumar-mutha. Ссылка на руководство по началу работы не содержит подробного описания этих фрагментов.
Джордж Пантазес,
15

Тесты MVC предназначены для охвата только части контроллера вашего приложения. HTTP-запросы и ответы имитируются, поэтому реальные соединения не создаются. С другой стороны, при использовании @SpringBootTestзагружается вся конфигурация контекста веб-приложения, и соединения проходят через реальный веб-сервер. В этом случае вы используете не MockMvcbean-компонент, а его стандарт RestTemplate(или новую альтернативу TestRestTemplate).

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

Это не значит, что вы не можете использовать mocks @SpringBootTest; если вы пишете интеграционный тест, это может быть необходимо. В любом случае лучше не использовать его только для простого юнит-теста контроллера.

источник - Изучение микросервисов с помощью Spring Boot

Гаутам Тадигоппула
источник
1
Я не понимаю, почему за этот ответ проголосовали .. Когда вы используете @SpringBootTest, настоящий веб-сервер не запускается, если у вас также нет webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT(или DEFINED_PORT) и соединения не проходят через настоящий веб-сервер. По умолчанию @SpringBootTestэто WebEnvironment.MOCK.
Корай Тугай,