【问题标题】:Issue with testing Spring MVC slice in SpringBoot 1.4在 Spring Boot 1.4 中测试 Spring MVC 切片的问题
【发布时间】:2016-10-31 07:00:55
【问题描述】:

我正在尝试新的 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 来测试控制器。我的观点是 thymeleaf teamplate。我的控制器测试是这样的:

@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() 建议,以便对控制器进行更彻底的测试。

提前致谢。

【问题讨论】:

    标签: spring-mvc spring-boot spring-test-mvc


    【解决方案1】:

    以防万一您添加了以下装饰器但仍然无法正常工作:

    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>
    

    【讨论】:

      【解决方案2】:

      我没有像这样在设置阶段实例化 mockmvc 对象,而不是自动连接 MockMvc。

      protected void setUp() {
              mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
          }
      

      【讨论】:

        【解决方案3】:

        有兴趣加载完整应用程序的人应尝试结合使用@SpringBootTest@AutoConfigureMockMvc 而不是@WebMvcTest

        我一直在为这个问题苦苦挣扎,但最终我得到了完整的画面。
        互联网上的许多教程,以及我目前找到的官方 Spring 文档,声明您可以使用 @WebMvcTest 测试您的控制器;这是完全正确的,但仍然省略了一半的故事。
        正如此类注释的 javadoc 所指出的,@WebMvcTest 仅用于测试您的控制器,根本不会加载您应用的所有 bean,这是设计使然。
        它甚至与 @Componentscan 这样的显式 bean 扫描注解不兼容。

        我建议任何对此事感兴趣的人阅读注释的完整 javadoc(只有 30 行长,并且塞满了浓缩的有用信息),但我会提取一些与我的情况相关的宝石。

        来自Annotation Type WebMvcTest

        使用此注解将禁用完全自动配置,而仅应用与 MVC 测试相关的配置(即 @Controller@ControllerAdvice@JsonComponent 过滤器、WebMvcConfigurerHandlerMethodArgumentResolver bean,但不是 @Component@Service@Repository 豆子)。 [...] 如果您希望加载完整的应用程序配置并使用 MockMVC,则应考虑将 @SpringBootTest@AutoConfigureMockMvc 结合使用,而不是此注释

        实际上,只有@SpringBootTest + @AutoConfigureMockMvc 解决了我的问题,所有其他使用@WebMvcTest 的方法都无法加载一些所需的bean。

        编辑

        我收回我对 Spring 文档所做的评论,因为我不知道使用 @WebMvcTest 时暗示了 slice;实际上,MVC 切片文档清楚地表明,并非所有应用程序都已加载,这是切片的本质。

        Custom test slice with Spring Boot 1.4

        测试切片是对为您的测试创建的 ApplicationContext 进行分段。通常,如果您想使用 MockMvc 测试控制器,您肯定不想打扰数据层。相反,您可能希望模拟控制器使用的服务并验证所有与 Web 相关的交互是否按预期工作。

        【讨论】:

        • 恰到好处。 @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc 用于测试类,@Autowired 用于private MockMvc mockMvc;
        • 你是救命的兄弟!
        【解决方案4】:

        您在使用@WebMvcTest 的同时还手动配置了MockMvc 实例。这没有任何意义,因为@WebMvcTest 的主要目的之一是为您自动配置MockMvc 实例。此外,在您的手动配置中,您使用的是standaloneSetup,这意味着您需要完全配置正在测试的控制器,包括将任何依赖项注入其中。您没有这样做会导致NullPointerException

        如果您想使用@WebMvcTest,我建议您这样做,您可以完全删除您的setUp 方法,并使用@Autowired 字段注入自动配置的MockMvc 实例。

        然后,要控制ProductController 使用的ProductService,您可以使用新的@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())));
        
            }
        }
        

        【讨论】:

        • @MockBean 添加到模拟ProductService 会引发NullPointerException,我已经在github 上发布了我的代码here
        • 我错过了您手动配置MockMvc 实例。我已经更新了答案
        • Autowiring MockMvc 似乎默认启用了安全性,因此报告java.lang.AssertionError: Status Expected :200 Actual :401。通过扩展WebSecurityConfigurerAdapter 和覆盖configure(HttpSecurity httpSecurity) 以允许所有对//products 的GET 请求来配置安全性也没有帮助。最后不得不通过控制器测试类上的@AutoConfigureMockMvc(secure=false) 禁用安全性,现在它就像一个魅力。非常感谢您解决了我测试 MVC 切片的核心问题。
        • 我的设置与您的示例完全一样,但我在Service 上获得了RepositoryNoSuchBeanDefinitionException。对于 @Controller 依赖项使用 @MockBean 是否需要额外的东西?
        • ^ NVM,看起来像一个未解决的问题。 github.com/spring-projects/spring-boot/issues/6663
        猜你喜欢
        • 2016-10-29
        • 2017-04-23
        • 2016-11-05
        • 2021-11-30
        • 2017-05-31
        • 2016-12-05
        • 1970-01-01
        • 2016-12-12
        相关资源
        最近更新 更多