Проблема с тестированием Spring среза MVC в SpringBoot 1.4
Я тестирую новые функции тестирования Spring Boot 1.4 MVC. У меня есть следующий контроллер.
@Controller
public class ProductController {
private ProductService productService;
@Autowired
public void setProductService(ProductService productService) {
this.productService = productService;
}
@RequestMapping(value = "/products", method = RequestMethod.GET)
public String list(Model model){
model.addAttribute("products", productService.listAllProducts());
return "products";
}
}
Моя минимальная реализация ProductService:
@Service
public class ProductServiceImpl implements ProductService {
private ProductRepository productRepository;
@Autowired
public void setProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Override
public Iterable<Product> listAllProducts() {
return productRepository.findAll();
}
}
Код ProductRepository:
public interface ProductRepository extends CrudRepository<Product,
Integer>{
}
Я пытаюсь использовать новый @WebMvcTest для проверки конроллера. Мое мнение - табличка с тимелеафом. И мой контрольный тест:
@RunWith(SpringRunner.class)
@WebMvcTest(ProductController.class)
public class ProductControllerTest {
private MockMvc mockMvc;
@Before
public void setUp() {
ProductController productController= new ProductController();
mockMvc = MockMvcBuilders.standaloneSetup(productController).build();
}
@Test
public void testList() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/products"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("products"))
.andExpect(MockMvcResultMatchers.model().attributeExists("products"));
}
}
Но при запуске теста я получаю эту ошибку.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productController': Unsatisfied dependency expressed through method 'setProductService' parameter 0: No qualifying bean of type [guru.springframework.services.ProductService] found for dependency [guru.springframework.services.ProductService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [guru.springframework.services.ProductService] found for dependency [guru.springframework.services.ProductService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
Мне нужна помощь, чтобы решить проблему, чтобы правильно протестировать ProductController. Предложения для дополнительных и Expect() для более тщательного тестирования контроллера будут высоко оценены.
Спасибо заранее.
Ответы
Ответ 1
Вы используете @WebMvcTest
, а также вручную настраиваете экземпляр MockMvc
. Это не имеет смысла, поскольку одной из основных целей @WebMvcTest
является автоматическая настройка экземпляра MockMvc
для вас. Кроме того, в вашей ручной конфигурации вы используете standaloneSetup
, что означает, что вам нужно полностью сконфигурировать тестируемый контроллер, включая вложение в него любых зависимостей. Вы не делаете то, что вызывает NullPointerException
.
Если вы хотите использовать @WebMvcTest
, и я бы порекомендовал вам это сделать, вы можете полностью удалить свой метод setUp
и иметь инсталлированный автоматически сконфигурированный экземпляр MockMvc
, используя поле @Autowired
.
Затем, чтобы управлять ProductService
, который использовался ProductController
, вы можете использовать новую аннотацию @MockBean
для создания макета ProductService
, который затем будет введен в ProductController
.
Эти изменения оставляют ваш тестовый класс следующим:
package guru.springframework.controllers;
import guru.springframework.services.ProductService;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@WebMvcTest(ProductController.class)
public class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ProductService productService;
@Test
public void testList() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/products"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("products"))
.andExpect(MockMvcResultMatchers.model().attributeExists("products"))
.andExpect(MockMvcResultMatchers.model().attribute("products",
Matchers.is(Matchers.empty())));
}
}
Ответ 2
@SpringBootTest
кто заинтересован в загрузке полного приложения, должны попытаться использовать @SpringBootTest
сочетании с @AutoConfigureMockMvc
а не @WebMvcTest
.
Я долго боролся с этой проблемой, но, наконец, я получил полную картину.
Во многих учебных руководствах в Интернете, а также в официальной документации Spring, которую я нашел до сих пор, @WebMvcTest
, что вы можете тестировать свои контроллеры, используя @WebMvcTest
; это совершенно правильно, все же опуская половину истории, хотя.
Как указано в javadoc такой аннотации, @WebMvcTest
предназначен только для тестирования ваших контроллеров и не загружает все компоненты вашего приложения вообще, и это @WebMvcTest
.
Это даже несовместимо с явными аннотациями сканирования компонентов, такими как @Componentscan
.
Я предлагаю всем, кто интересуется этим вопросом, прочитать полный Javadoc аннотации (длиной всего 30 строк и заполненный сжатой полезной информацией), но я извлеку пару драгоценных камней, относящихся к моей ситуации.
из типа аннотации WebMvcTest
Используя эту аннотацию отключит полностью автоматическую конфигурацию и вместо того, чтобы применять только конфигурацию, имеющую отношение к тестам MVC (т.е. @Controller
, @ControllerAdvice
, @JsonComponent
фильтр, WebMvcConfigurer
и HandlerMethodArgumentResolver
бобы, но не @Component
, @Service
или @Repository
бобы). [...] Если вы хотите загрузить полную конфигурацию приложения и использовать MockMVC, вам следует рассмотреть @SpringBootTest
сочетании с @AutoConfigureMockMvc
а не эту аннотацию.
И на самом деле, только @SpringBootTest
+ @AutoConfigureMockMvc
исправили мою проблему, все другие подходы, использующие @WebMvcTest
не смогли загрузить некоторые необходимые компоненты.
РЕДАКТИРОВАТЬ
Я забрал свой комментарий о документации Spring, потому что я не знал, что подразумевается срез, когда используется @WebMvcTest
; на самом деле в документации по слайсам MVC ясно, что загружается не все приложение, что по самой природе слайса.
Пользовательский тестовый срез с Spring Boot 1.4
Тестовая нарезка - это сегментирование ApplicationContext, созданного для вашего теста. Как правило, если вы хотите протестировать контроллер с помощью MockMvc, вы наверняка не хотите беспокоиться о слое данных. Вместо этого вы, вероятно, захотите смоделировать службу, которую использует ваш контроллер, и проверить, что все веб-взаимодействия работают так, как ожидалось.
Ответ 3
Вместо автоматической разводки MockMvc я создал экземпляр объекта mockmvc на этапе настройки следующим образом.
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}