在SpringBoot 1.4中测试Spring MVC切片的问题

use*_*135 14 spring-mvc spring-test-mvc spring-boot

我正在尝试新的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";
  }
}
Run Code Online (Sandbox Code Playgroud)

我最小的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();
  }

}
Run Code Online (Sandbox Code Playgroud)

ProductRepository的代码是:

public interface ProductRepository extends CrudRepository<Product,    
 Integer>{
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用新的@WebMvcTest来测试控制器.我的观点是百里香的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"));               
 }
}
Run Code Online (Sandbox Code Playgroud)

但是,在运行测试时我得到了这个错误.

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: {}
Run Code Online (Sandbox Code Playgroud)

我需要帮助来解决问题以正确测试ProductController.建议额外的andExpect()以更全面地测试控制器将受到高度赞赏.

提前致谢.

Ant*_*nio 21

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

我一直在努力解决这个问题,但最后我得到了完整的图片.
互联网上的许多教程, 以及我到目前为止发现的官方Spring文档,都说明你可以使用测试你的控制器@WebMvcTest; 这完全正确,但仍然省略了一半的故事.
正如这种注释的javadoc所指出的那样,@WebMvcTest仅用于测试您的控制器,并且根本不会加载所有应用程序的bean,这是设计的.
它甚至与显式的bean扫描注释不兼容@Componentscan.

我建议有人对这个问题感兴趣,阅读注释的完整javadoc(只有30行长,并且包含了浓缩的有用信息),但我将提取一些与我的情况相关的宝石.

来自注释类型WebMvcTest

使用此批注将全面禁用自动配置,改为适用有关MVC测试(即只配置@Controller,@ControllerAdvice,@JsonComponent过滤器,WebMvcConfigurerHandlerMethodArgumentResolver咖啡豆,但没有@Component,@Service@Repository豆类).[...] 如果您要加载完整的应用程序配置并使用MockMVC,则应考虑@SpringBootTest@AutoConfigureMockMvc此注释结合使用而不是此注释.

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

编辑

我收回了我对Spring文档所做的评论,因为当我使用a时,我并不知道有一个片段是隐含的@WebMvcTest; 实际上MVC切片文档清楚地表明并非所有应用程序都被加载,这是切片的本质.

使用Spring Boot 1.4自定义测试片

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


And*_*son 15

您正在使用的@WebMvcTest同时还手动配置MockMvc实例.这没有意义,因为其中一个主要目的@WebMvcTestMockMvc为您自动配置实例.此外,在您使用的手动配置中,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())));

    }
}
Run Code Online (Sandbox Code Playgroud)