Spring开机如何测试WebServerInitialized事件
Spring boot how to test WebServerInitialized event
我正在尝试测试应用程序向服务注册表注册时发生的应用程序功能。只有当应用程序具有完整的 Web 上下文时才会发生这种情况(即 spring-boot-starter-web
在 class 路径上,并且 servlet 未被模拟)。这是通过 spring-cloud-commons
AbstractAutoServiceRegistration.
控制的
简单测试
所有测试应该做的是:
1) Bring up Web App
2) Verify auto-registration w/ service registry event fired
3) Manually force close app
4) Verify auto-deregistratoin occurred
方法一:@SpringBootTest
SpringBootTest
可以轻松创建完整的 Web 上下文,这很棒。但是我无法在测试中关闭应用程序以强制取消注册
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = MyAutoConfig.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT
)
@EnableAutoConfiguration
public class DiscoverySpringCloudBootMinimalRegistrationTest {
@Test
public void register_deregister {
// Force-close app to trigger dereigster (causes exception)
((ConfigurableApplicationContext) context).close();
verify(registry, times(1)).register(autoRegistrationServiceRecord);
verify(registry, times(1)).deregister(autoRegistrationServiceRecord);
}
context.close()
调用导致了一个很长的错误,基本上是说不要像这样手动关闭上下文。
..... contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]] is not active. This may be due to one of the following reasons: 1) the context was closed programmatically by user code; 2) the context was closed during parallel test execution either according to @DirtiesContext semantics or due to automatic eviction from the ContextCache due to a maximum cache size policy.
方法二:WebContextRunner
在这种方法中,我避免了 @SpringBootTest
并手动配置上下文运行器。这对于调用 context.close()
非常有效,但是配置中的 Web 上下文有一个模拟 servlet,并且不会触发自动注册所需的 WebInitializedEvent
。
public class BasicAutoConfigTests {
private WebApplicationContextRunner runner;
@Test
public void register_deregister() {
runner = new WebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(MyAutoConfig.class));
runner.run((context) -> {
assertThat(context).hasNotFailed();
ServiceRegistry registry = context.getBean(ServiceRegistry.class);
ServiceRecord autoRegistration = context.getBean(MyServiceRecord.class);
context.close();
verify(registry, times(1)).register(autoRegistration);
verify(registry, times(1)).deregister(autoRegistration);
});
}
这几乎可以工作 但会产生一个 MockServletContext
bean,我认为它无法从 spring-cloud-commons
触发必要的 WebServerInitializedEvent
。这种方法如何 bootstrap 一个真实的、完整的嵌入式 tomcat 服务器?
听从 Spencer 的建议,我使用 spring 应用程序构建器创建了一个完整的网络应用程序。我也在自动配置模块之外做了这个 - 在类路径上创建了一个名为 "integration tests" 和 spring-boot-starter-web
的新 Maven 子模块。
@Import(MyAutoConfig.class)
@SpringBootApplication
public class MinStarterBasicApp {
@Bean
ServiceRegistry serviceRegistry() {
return mock(ServiceRegistry.class);
}
static ConfigurableApplicationContext setupWebApp(String... profiles){
System.setProperty("spring.main.allow-bean-definition-overriding", "true");
SpringApplication app = new SpringApplicationBuilder(MinStarterBasicApp.class)
.web(WebApplicationType.SERVLET)
.profiles(profiles)
.build();
return app.run();
}
}
其中 profiles
允许我按名称传递 application.properties 文件,如下所示。同样重要的是要确保我们手动 close
每个测试的应用上下文。
public class StarterBasicAutoconfigTest {
ConfigurableApplicationContext context;
@After
public void teardown() {
if (context != null && context.isRunning())
context.close();
}
@Test
public void sometest() {
context = MinStarterBasicApp.setupWebApp("profile1");
ServiceRegistry registry = context.getBean(ServiceRegistry.class);
context.close();
Mockito.verify(registry, times(1)).register(any());
Mockito.verify(registry, times(1)).deregister(any());
}
我正在尝试测试应用程序向服务注册表注册时发生的应用程序功能。只有当应用程序具有完整的 Web 上下文时才会发生这种情况(即 spring-boot-starter-web
在 class 路径上,并且 servlet 未被模拟)。这是通过 spring-cloud-commons
AbstractAutoServiceRegistration.
简单测试
所有测试应该做的是:
1) Bring up Web App
2) Verify auto-registration w/ service registry event fired
3) Manually force close app
4) Verify auto-deregistratoin occurred
方法一:@SpringBootTest
SpringBootTest
可以轻松创建完整的 Web 上下文,这很棒。但是我无法在测试中关闭应用程序以强制取消注册
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = MyAutoConfig.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT
)
@EnableAutoConfiguration
public class DiscoverySpringCloudBootMinimalRegistrationTest {
@Test
public void register_deregister {
// Force-close app to trigger dereigster (causes exception)
((ConfigurableApplicationContext) context).close();
verify(registry, times(1)).register(autoRegistrationServiceRecord);
verify(registry, times(1)).deregister(autoRegistrationServiceRecord);
}
context.close()
调用导致了一个很长的错误,基本上是说不要像这样手动关闭上下文。
..... contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]] is not active. This may be due to one of the following reasons: 1) the context was closed programmatically by user code; 2) the context was closed during parallel test execution either according to @DirtiesContext semantics or due to automatic eviction from the ContextCache due to a maximum cache size policy.
方法二:WebContextRunner
在这种方法中,我避免了 @SpringBootTest
并手动配置上下文运行器。这对于调用 context.close()
非常有效,但是配置中的 Web 上下文有一个模拟 servlet,并且不会触发自动注册所需的 WebInitializedEvent
。
public class BasicAutoConfigTests {
private WebApplicationContextRunner runner;
@Test
public void register_deregister() {
runner = new WebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(MyAutoConfig.class));
runner.run((context) -> {
assertThat(context).hasNotFailed();
ServiceRegistry registry = context.getBean(ServiceRegistry.class);
ServiceRecord autoRegistration = context.getBean(MyServiceRecord.class);
context.close();
verify(registry, times(1)).register(autoRegistration);
verify(registry, times(1)).deregister(autoRegistration);
});
}
这几乎可以工作 但会产生一个 MockServletContext
bean,我认为它无法从 spring-cloud-commons
触发必要的 WebServerInitializedEvent
。这种方法如何 bootstrap 一个真实的、完整的嵌入式 tomcat 服务器?
听从 Spencer 的建议,我使用 spring 应用程序构建器创建了一个完整的网络应用程序。我也在自动配置模块之外做了这个 - 在类路径上创建了一个名为 "integration tests" 和 spring-boot-starter-web
的新 Maven 子模块。
@Import(MyAutoConfig.class)
@SpringBootApplication
public class MinStarterBasicApp {
@Bean
ServiceRegistry serviceRegistry() {
return mock(ServiceRegistry.class);
}
static ConfigurableApplicationContext setupWebApp(String... profiles){
System.setProperty("spring.main.allow-bean-definition-overriding", "true");
SpringApplication app = new SpringApplicationBuilder(MinStarterBasicApp.class)
.web(WebApplicationType.SERVLET)
.profiles(profiles)
.build();
return app.run();
}
}
其中 profiles
允许我按名称传递 application.properties 文件,如下所示。同样重要的是要确保我们手动 close
每个测试的应用上下文。
public class StarterBasicAutoconfigTest {
ConfigurableApplicationContext context;
@After
public void teardown() {
if (context != null && context.isRunning())
context.close();
}
@Test
public void sometest() {
context = MinStarterBasicApp.setupWebApp("profile1");
ServiceRegistry registry = context.getBean(ServiceRegistry.class);
context.close();
Mockito.verify(registry, times(1)).register(any());
Mockito.verify(registry, times(1)).deregister(any());
}