我的目标是将以前使用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)
我该如何处理?我是否应始终使用@SpringBootApplication注释类以运行@WebMvcTest测试?
编辑1:我做了一个包含2个模块和最小配置的小型maven项目。它是here。现在,我获得了另一个模块中定义的存储库的NoSuchBeanDefinitionException异常。如果我配置&#34;完整&#34; @SpringBootApplication - 一切都很好。
编辑2:我从编辑1修改了小测试项目以给出原始问题。我正在玩不同的注释并在配置类上添加了@ComponentScan,因为我怀疑bean没有正确注册。但是,我希望只有@Controller bean(在@WebMvcTest(... class)中定义)才能根据@WebMvcTest行为背后的魔法进行注册。
编辑3:Spring Boot项目issue。
答案 0 :(得分:1)
简短回答:我相信。
答案很长:
我相信@WebMvcTest
需要找到SpringBootApplication配置,因为WebMvcTest
的唯一目的是帮助简化测试(SpringBootApplication
宁愿尝试加载整个世界。)
在您的特定情况下,由于您的非测试包中没有任何内容,我相信它还会找到SampleTestConfiguration,它使用@ScanPackages
注释并以某种方式加载每个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, Filter,WebMvcConfigurer和HandlerMethodArgumentResolver。定期 使用此批注时,不会扫描@Component bean。
WebMvcTest
需要SpringBootApplication
:WebMvcTest
继承了许多自动配置,因此需要SpringBoot来加载它们。然后它会禁用许多其他自动配置,并且您的控制器变得易于测试。
使用WebMvcTest
的全部意义在于您拥有SpringBootApplication
,并希望通过禁用除控制器之外的所有bean来简化测试。如果您没有SpringBootApplication,那么为什么要使用WebMvcTest呢?
答案 1 :(得分:1)
是的,根据spring boot docs
搜索算法从包含测试的包开始工作,直到找到@SpringBootApplication或@SpringBootConfiguration注释类。只要您以合理的方式构建代码,通常就可以找到主要配置。
但是在我使用@WebMvcTest之后,spring boot仍然尝试加载其他bean,最后TypeExcludeFilter
完成了这个技巧。
```java
@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{
}
}
```
答案 2 :(得分:0)
这是一个古老的话题,但是这里没有提到解决方案。
您可以仅在测试源中创建一个用SpringBootApplication
注释的类。这样,您的项目仍将具有一个不错的,多模块的结构,只有一个“真实的” SpringBootApplication
。