在 SpringBoot 1.4 中测试 Spring MVC 切片时出现问题
Issue with testing Spring MVC slice in 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。非常感谢有关额外 andExpect() 的建议,以便对控制器进行更彻底的测试。
提前致谢。
您正在使用 @WebMvcTest
,同时还手动配置了一个 MockMvc
实例。这没有意义,因为 @WebMvcTest
的主要目的之一是为您自动配置 MockMvc
实例。此外,在您的手动配置中,您使用的是 standaloneSetup
,这意味着您需要完全配置正在测试的控制器,包括向其中注入任何依赖项。你没有做导致 NullPointerException
.
的事情
如果你想使用 @WebMvcTest
,我建议你这样做,你可以完全删除你的 setUp
方法并注入一个自动配置的 MockMvc
实例,而不是使用@Autowired
字段。
然后,要控制 ProductController
使用的 ProductService
,您可以使用新的 @MockBean
注释创建一个模拟 ProductService
,然后将其注入ProductController
.
这些更改使您的测试 class 看起来像这样:
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())));
}
}
有兴趣加载完整应用程序的人应该尝试结合使用 @SpringBootTest
和 @AutoConfigureMockMvc
而不是 @WebMvcTest
.
我已经为这个问题苦苦挣扎了一段时间,但最终我得到了全貌。
互联网上的许多教程 以及我目前找到的官方 Spring 文档 声明您可以使用 @WebMvcTest
测试您的控制器;完全正确,不过仍然省略了一半的故事。
正如此类注释的 javadoc 所指出的,@WebMvcTest
仅用于测试您的控制器,根本不会加载您应用程序的所有 bean,这是设计使然.
它甚至与 @Componentscan
.
这样的显式 bean 扫描注释不兼容
我建议任何对此事感兴趣的人阅读注释的完整 javadoc(它只有 30 行长,包含浓缩的有用信息),但我会提取一些与我的情况相关的精华。
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. @Controller
, @ControllerAdvice
, @JsonComponent
Filter, WebMvcConfigurer
and HandlerMethodArgumentResolver
beans but not @Component
, @Service
or @Repository
beans). [...]
If you are looking to load your full application configuration and use MockMVC, you should consider @SpringBootTest
combined with @AutoConfigureMockMvc
rather than this annotation.
实际上,只有 @SpringBootTest
+ @AutoConfigureMockMvc
解决了我的问题,所有其他使用 @WebMvcTest
的方法都无法加载一些所需的 bean。
编辑
我收回我对 Spring 文档的评论,因为我不知道 切片 在使用 @WebMvcTest
时暗示;实际上,MVC slice 文档明确指出并非所有应用程序都已加载,这是 slice 的本质。
Custom test slice with Spring Boot 1.4
Test slicing is about segmenting the ApplicationContext that is created for your test. Typically, if you want to test a controller using MockMvc, surely you don’t want to bother with the data layer. Instead you’d probably want to mock the service that your controller uses and validate that all the web-related interaction works as expected.
我在设置阶段像这样实例化了 mockmvc 对象,而不是自动连接 MockMvc。
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
以防万一您添加了以下装饰器但它仍然无法正常工作:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SomeTest {
@Autowired
private MockMvc mockMvc;
@Test
public void somePositiveTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get(url))
.andExpect(status().is2xxSuccessful());
}
}
确保您已将以下依赖项添加到您的 pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
我正在尝试新的 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。非常感谢有关额外 andExpect() 的建议,以便对控制器进行更彻底的测试。
提前致谢。
您正在使用 @WebMvcTest
,同时还手动配置了一个 MockMvc
实例。这没有意义,因为 @WebMvcTest
的主要目的之一是为您自动配置 MockMvc
实例。此外,在您的手动配置中,您使用的是 standaloneSetup
,这意味着您需要完全配置正在测试的控制器,包括向其中注入任何依赖项。你没有做导致 NullPointerException
.
如果你想使用 @WebMvcTest
,我建议你这样做,你可以完全删除你的 setUp
方法并注入一个自动配置的 MockMvc
实例,而不是使用@Autowired
字段。
然后,要控制 ProductController
使用的 ProductService
,您可以使用新的 @MockBean
注释创建一个模拟 ProductService
,然后将其注入ProductController
.
这些更改使您的测试 class 看起来像这样:
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())));
}
}
有兴趣加载完整应用程序的人应该尝试结合使用 @SpringBootTest
和 @AutoConfigureMockMvc
而不是 @WebMvcTest
.
我已经为这个问题苦苦挣扎了一段时间,但最终我得到了全貌。
互联网上的许多教程 以及我目前找到的官方 Spring 文档 声明您可以使用 @WebMvcTest
测试您的控制器;完全正确,不过仍然省略了一半的故事。
正如此类注释的 javadoc 所指出的,@WebMvcTest
仅用于测试您的控制器,根本不会加载您应用程序的所有 bean,这是设计使然.
它甚至与 @Componentscan
.
我建议任何对此事感兴趣的人阅读注释的完整 javadoc(它只有 30 行长,包含浓缩的有用信息),但我会提取一些与我的情况相关的精华。
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e.
@Controller
,@ControllerAdvice
,@JsonComponent
Filter,WebMvcConfigurer
andHandlerMethodArgumentResolver
beans but not@Component
,@Service
or@Repository
beans). [...] If you are looking to load your full application configuration and use MockMVC, you should consider@SpringBootTest
combined with@AutoConfigureMockMvc
rather than this annotation.
实际上,只有 @SpringBootTest
+ @AutoConfigureMockMvc
解决了我的问题,所有其他使用 @WebMvcTest
的方法都无法加载一些所需的 bean。
编辑
我收回我对 Spring 文档的评论,因为我不知道 切片 在使用 @WebMvcTest
时暗示;实际上,MVC slice 文档明确指出并非所有应用程序都已加载,这是 slice 的本质。
Custom test slice with Spring Boot 1.4
Test slicing is about segmenting the ApplicationContext that is created for your test. Typically, if you want to test a controller using MockMvc, surely you don’t want to bother with the data layer. Instead you’d probably want to mock the service that your controller uses and validate that all the web-related interaction works as expected.
我在设置阶段像这样实例化了 mockmvc 对象,而不是自动连接 MockMvc。
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
以防万一您添加了以下装饰器但它仍然无法正常工作:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SomeTest {
@Autowired
private MockMvc mockMvc;
@Test
public void somePositiveTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get(url))
.andExpect(status().is2xxSuccessful());
}
}
确保您已将以下依赖项添加到您的 pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>