向“@WebMvcTest”添加一个额外的 bean

Add one additional bean to "@WebMvcTest"

我有一个控制器和一个使用@WebMvcTest 的测试,它 运行 很好。现在我需要添加一些验证逻辑,为此我 @Autowired 一个额外的 bean(一个 @Component,一个 MapstructMapper)。

正如预期的那样,由于 @WebMvcTest,现在测试失败了。 (未发现组件)

有没有办法将一个 bean 添加到创建的上下文中?

由于我使用 @MockBeans 模拟服务层:有没有办法将所有模拟调用委托给真实对象?有了这个我可以模拟映射器并委托给真正的映射器?!

在上下文中获取额外 bean 的一种简单方法是通过在测试 classes

中使用嵌套配置 classes
@TestConfiguration
static class AdditionalConfig {
    @Bean
    public SomeBean getSomeBean() {
        return new SomeBean());
    }
}

示例:

场景 - 如果你有一些控制器说 ProductController 并且你有相应的 slice-test 用于 class 说 ProductionControllerTest

@RestController
public class ProductController {

    @Autowired
    private IProductService productService;

    @Autowired
    private IProductValidator productValidator;


    @GetMapping("product")
    public Product getProduct(@RequestParam Long id) {

        Product product = productService.getProduct(id); // you are using mockBean of productService

        productValidator.validateProduct(product); // you need real bean of productValidator
        return product;
    }
}

相应的幻灯片测试class 带有额外的 bean 配置

@RunWith(SpringRunner.class)
@WebMvcTest
public class ProductControllerSliceTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private IProductService productService;

    @Autowired
    private ApplicationContext applicationContext;

    @TestConfiguration
    static class AdditionalConfig {
        @Bean
        public IProductValidator productValidator() {
            return new ProductValidator();
        }
    }


    @Test
    public void testProductGetById() throws Exception {
        Product testProductWithID1L = new Product(1L, "testProduct");
        when(productService.getProduct(anyLong())).thenReturn(testProductWithID1L);

        mockMvc.perform(get("/product")
                .param("id", "1")).andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("name")
                        .value("testProduct"))
                .andExpect(MockMvcResultMatchers.jsonPath("id")
                        .value("1"));
    }
}

关于您的场景的其他想法: 如果您真的打算对控制器进行单元测试 class 那么理想情况下您应该模拟 class 你正在测试。 理想情况下,单元测试的目的是只测试被测 object/class 的行为。所有相关的 classes 行为或外部调用都应该被模拟。
当您开始在一个测试下一起测试多个 class 时,您将更多地转向组件测试或集成测试

一个非常简单的解决方案是用 @Import 注释您的测试 class 指定要在测试中使用的其他 bean 的 class(es),作为 stated in documentation:

Typically @WebMvcTest is used in combination with @MockBean or @Import to create any collaborators required by your @Controller beans.

例如

@WebMvcTest(MyController.class)
@Import(SomeOtherBean.class)
public class SourcingOrganisationControllerTests {

    // The controller bean is made available via @WebMvcTest  
    @Autowired
    private MyController myController;

    // Additional beans (only one in this case) are made available via @Import
    @Autowired
    private SomeOtherBean someOtherBean;
}