【问题标题】:Does @WebMvcTest require @SpringBootApplication annotation?@WebMvcTest 是否需要 @SpringBootApplication 注释?
【发布时间】:2016-12-17 20:57:02
【问题描述】:

我的目标是将之前使用 Spring Boot 1.3 开发的 Spring Boot 应用程序迁移到最新的 Spring Boot 版本 1.4。该应用程序由几个 maven 模块组成,其中只有一个包含带有 @SpringBootApplication 注释的类。

迁移的一部分是使用@WebMvcTest 注释来有效地测试控制器,这里我遇到了一个问题。

考虑来自 Spring Boot github 页面的 example application@WebMvcTest 注释工作得很好,因为据我了解(在我做了几次测试之后),主包中有一个用@SpringBootApplication 注释的类。请注意,对于我自己的 @WebMvcTest 测试,我遵循与上面示例中所示相同的概念。

我看到的唯一区别是,在我的应用程序中,控制器类位于单独的 maven 模块中(没有 @SpringBootApplication 注释类),但具有 @Configuration 和 SpringBootConfiguration 配置。如果我不使用@SpringBootApplication 注释任何类,我在测试控制器时总是会得到一个断言。我的断言与上面示例中的 SampleTestApplication 类修改为只有 @EnableAutoConfiguration@SpringBootConfiguration 注释时相同(@SpringBootApplication 不存在):

getVehicleWhenRequestingTextShouldReturnMakeAndModel(sample.test.web.UserVehicleControllerTests)  Time elapsed: 0.013 sec  <<< FAILURE!
java.lang.AssertionError: Status expected:<200> but was:<404>
    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54)
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:81)
    at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.java:664)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
   at sample.test.web.UserVehicleControllerTests.getVehicleWhenRequestingTextShouldReturnMakeAndModel(UserVehicleControllerTests.java:68)

我应该如何处理?为了运行 @WebMvcTest 测试,我是否应该始终使用 @SpringBootApplication 注释类?

编辑 1:我做了一个带有 2 个模块和最小配置的小型 maven 项目。它是here。现在,我得到另一个模块中定义的存储库的 NoSuchBeanDefinitionException 异常。如果我配置“完整”@SpringBootApplication - 一切都很好。

编辑 2:我从 EDIT 1 修改了小型测试项目以给出原始问题。我正在使用不同的注释并在配置类上添加了@ComponentScan,因为我怀疑 bean 没有正确注册。但是,我希望只有 @Controller bean(在 @WebMvcTest(...class) 中定义)应基于 @WebMvcTest 行为背后的魔法进行注册。

编辑 3:Spring Boot 项目issue

【问题讨论】:

  • 我让你的小项目通过了测试。不确定这是否足以回答您的问题,或者您是否需要移动一些类以更好地代表问题。
  • @ShawnClark,感谢您的尝试。您将@SpringBootApplication 添加到配置类。可能是,我之前并不完全清楚,但这个技巧也适用于我。我希望在我的模块中不需要输入@SpringBootApplication

标签: spring-boot


【解决方案1】:

简短回答:我相信。

长答案:

我相信@WebMvcTest 需要找到 SpringBootApplication 配置,因为WebMvcTest 的唯一目的是帮助简化测试(SpringBootApplication 宁愿尝试加载整个世界)。

在您的具体情况下,由于您的非测试包中没有任何包,我相信它还会找到带有 @ScanPackages 注释的 SampleTestConfiguration 并以某种方式加载每个 bean。

src/main/java/sample/test中添加以下内容

@SpringBootApplication
public class SampleTestConfiguration {

}

并将您的测试更改为:

@RunWith(SpringRunner.class)
@WebMvcTest(MyController.class)
public class MyControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private MyService ms;

    @Autowired
    private ApplicationContext context;

    @Test
    public void getDataAndExpectOkStatus() throws Exception {
        given(ms.execute("1")).willReturn(false);
        mvc.perform(get("/1/data").accept(MediaType.APPLICATION_JSON_VALUE)).andExpect(status().isOk()).andExpect(content().string("false"));
    }

    @Test
    public void testMyControllerInAppCtx() {
        assertThat(context.getBean(MyController.class), is(not(nullValue())));
    }

    @Test
    public void testNoMyAnotherControllerInAppCtx() {
        try {
            context.getBean(MyAnotherController.class);
            fail("Bean exists");
        } catch (BeansException e) {
            // ok
        }
    }
}

@WebMvcTest 找到SpringBootApplication,然后仅加载有限数量的bean(参见documentation):

@WebMvcTest 将自动配置 Spring MVC 基础设施和 将扫描的 bean 限制为 @Controller、@ControllerAdvice、@JsonComponent、 过滤器、WebMvcConfigurer 和 HandlerMethodArgumentResolver。常规的 使用此注解时不会扫描@Component bean。

WebMvcTest需要SpringBootApplicationWebMvcTest继承了很多AutoConfiguration,所以需要SpringBoot来加载。然后它会禁用许多其他 AutoConfiguration 并且您的控制器变得易于测试。

使用WebMvcTest 的全部意义在于,当您拥有SpringBootApplication 并且您希望通过禁用除控制器之外的所有bean 来简化测试时。如果你没有 SpringBootApplication,那为什么还要使用 WebMvcTest?

【讨论】:

    【解决方案2】:

    这是一个老话题,但这里没有提到一个解决方案。

    您可以在测试源中创建一个带有SpringBootApplication 注释的类。然后,您的项目仍然有一个不错的多模块结构,只有一个“真实的”SpringBootApplication

    【讨论】:

    • 您需要使用 @ContextConfiguration(classes = TestSpringBootApplication.class) 注释您的类,以便指定自定义 @SpringBootApplication 进行测试。
    • 感谢@EugeneMaysyuk。这有帮助
    【解决方案3】:

    是的,根据spring boot docs

    搜索算法从包含测试的包开始,直到找到@SpringBootApplication 或@SpringBootConfiguration 注释类。只要您以合理的方式构建代码,通常就能找到您的主要配置。

    但是在我开始使用@WebMvcTest之后,spring boot仍然尝试加载其他bean,最后TypeExcludeFilter成功了。

        @RunWith(SpringRunner.class)
        @WebMvcTest(controllers = {JzYsController.class} )
        public class JzYsControllerTest {
    
            private static final String REST_V4_JZYS = "/rest/v4/JzYs";
    
            @Autowired
            private MockMvc mockMvc;
    
            @MockBean
            private JzYsService service;
    
            @Test
            public void deleteYsByMlbh() throws Exception {
                Mockito.when(service.deleteYsByMlbh(Mockito.anyString())).thenReturn(Optional.of(1));
                mockMvc.perform(delete(REST_V4_JZYS + "?mbbh=861FA4B0E40F5C7FECAF09C150BF3B01"))
                .andExpect(status().isNoContent());
            }
    
            @SpringBootConfiguration
            @ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
            public static class config{
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2020-03-09
      • 1970-01-01
      • 2023-03-08
      • 2019-02-16
      • 2022-06-14
      • 1970-01-01
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多