将外部 属性 传递给 JUnit 的扩展 class
Passing an external property to JUnit's extension class
我的 Spring 引导项目使用 JUnit 5。我想设置一个需要启动本地 SMTP 服务器的集成测试,所以我实现了一个自定义扩展:
public class SmtpServerExtension implements BeforeAllCallback, AfterAllCallback {
private GreenMail smtpServer;
private final int port;
public SmtpServerExtension(int port) {
this.port = port;
}
@Override
public void beforeAll(ExtensionContext extensionContext) {
smtpServer = new GreenMail(new ServerSetup(port, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
}
@Override
public void afterAll(ExtensionContext extensionContext) {
smtpServer.stop();
}
}
因为我需要配置服务器的端口我在测试中注册扩展 class 像这样:
@SpringBootTest
@AutoConfigureMockMvc
@ExtendWith(SpringExtension.class)
@ActiveProfiles("test")
public class EmailControllerIT {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Value("${spring.mail.port}")
private int smtpPort;
@RegisterExtension
// How can I use the smtpPort annotated with @Value?
static SmtpServerExtension smtpServerExtension = new SmtpServerExtension(2525);
private static final String RESOURCE_PATH = "/mail";
@Test
public void whenValidInput_thenReturns200() throws Exception {
mockMvc.perform(post(RESOURCE_PATH)
.contentType(APPLICATION_JSON)
.content("some content")
).andExpect(status().isOk());
}
}
虽然这基本上是有效的:我如何使用带有 @Value
注释的 smtpPort
(从 test
配置文件中读取)?
更新 1
根据您的建议,我创建了一个自定义 TestExecutionListener
。
public class CustomTestExecutionListener implements TestExecutionListener {
@Value("${spring.mail.port}")
private int smtpPort;
private GreenMail smtpServer;
@Override
public void beforeTestClass(TestContext testContext) {
smtpServer = new GreenMail(new ServerSetup(smtpPort, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
};
@Override
public void afterTestClass(TestContext testContext) {
smtpServer.stop();
}
}
监听器是这样注册的:
@TestExecutionListeners(value = CustomTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
当 运行 调用侦听器的测试时,但 smtpPort
始终是 0
,因此似乎 @Value
注释未被拾取。
我认为你不应该在这里使用扩展,或者一般来说,任何“raw-level”JUnit 东西(比如生命周期方法),因为你将无法访问应用程序上下文他们将无法对 bean 等执行任何自定义逻辑。
相反,看看 Spring's test execution listeners abstraction
通过这种方法,GreenMail
将成为一个由 spring 管理的 bean(可能在一个特殊的配置中,只在测试中加载)但是由于它成为一个 bean,它将能够加载 属性 值并使用 @Value
注释。
在测试执行侦听器中,您将在测试前启动服务器并在测试后停止(或者整个测试 class 如果您需要 - 它具有用于此的“挂钩”)。
另一方面,请确保您 mergeMode = MergeMode.MERGE_WITH_DEFAULTS
作为 @TestExecutionListeners
注释的参数,否则某些默认行为(例如测试中的自动装配、脏上下文(如果有)等)不会工作。
更新 1
关注问题中的更新 1。这不会起作用,因为侦听器本身不是 spring bean,因此您不能在侦听器本身中自动装配或使用 @Value
注释。
您可以尝试遵循 这可能会有帮助,但最初我的意思不同:
- 使 GreenMail 本身成为一个 bean:
@Configuration
// since you're using @SpringBootTest annotation - it will load properties from src/test/reources/application.properties so you can put spring.mail.port=1234 there
public class MyTestMailConfig {
@Bean
public GreenMail greenMail(@Value(${"spring.mail.port"} int port) {
return new GreenMail(port, ...);
}
}
现在可以将此配置放在src/test/java/<sub-package-of-main-app>/
中,这样在生产中根本不会加载它
- 现在测试执行侦听器只能用于 运行 启动/停止
GreenMail
服务器(据我了解你想在测试前启动它并在测试后停止,否则你根本不需要这些听众 :) )
public class CustomTestExecutionListener implements TestExecutionListener {
@Override
public void beforeTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.start();
}
@Override
public void afterTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.stop();
}
}
另一种选择是自动装配 GreenMail
bean 并使用 JUnit 的 @BeforeEach
和 @AfterEach
方法,但在这种情况下,您必须在不同的测试中复制此逻辑 classes 需要这种行为。监听器允许重用代码。
我的 Spring 引导项目使用 JUnit 5。我想设置一个需要启动本地 SMTP 服务器的集成测试,所以我实现了一个自定义扩展:
public class SmtpServerExtension implements BeforeAllCallback, AfterAllCallback {
private GreenMail smtpServer;
private final int port;
public SmtpServerExtension(int port) {
this.port = port;
}
@Override
public void beforeAll(ExtensionContext extensionContext) {
smtpServer = new GreenMail(new ServerSetup(port, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
}
@Override
public void afterAll(ExtensionContext extensionContext) {
smtpServer.stop();
}
}
因为我需要配置服务器的端口我在测试中注册扩展 class 像这样:
@SpringBootTest
@AutoConfigureMockMvc
@ExtendWith(SpringExtension.class)
@ActiveProfiles("test")
public class EmailControllerIT {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Value("${spring.mail.port}")
private int smtpPort;
@RegisterExtension
// How can I use the smtpPort annotated with @Value?
static SmtpServerExtension smtpServerExtension = new SmtpServerExtension(2525);
private static final String RESOURCE_PATH = "/mail";
@Test
public void whenValidInput_thenReturns200() throws Exception {
mockMvc.perform(post(RESOURCE_PATH)
.contentType(APPLICATION_JSON)
.content("some content")
).andExpect(status().isOk());
}
}
虽然这基本上是有效的:我如何使用带有 @Value
注释的 smtpPort
(从 test
配置文件中读取)?
更新 1
根据您的建议,我创建了一个自定义 TestExecutionListener
。
public class CustomTestExecutionListener implements TestExecutionListener {
@Value("${spring.mail.port}")
private int smtpPort;
private GreenMail smtpServer;
@Override
public void beforeTestClass(TestContext testContext) {
smtpServer = new GreenMail(new ServerSetup(smtpPort, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
};
@Override
public void afterTestClass(TestContext testContext) {
smtpServer.stop();
}
}
监听器是这样注册的:
@TestExecutionListeners(value = CustomTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
当 运行 调用侦听器的测试时,但 smtpPort
始终是 0
,因此似乎 @Value
注释未被拾取。
我认为你不应该在这里使用扩展,或者一般来说,任何“raw-level”JUnit 东西(比如生命周期方法),因为你将无法访问应用程序上下文他们将无法对 bean 等执行任何自定义逻辑。
相反,看看 Spring's test execution listeners abstraction
通过这种方法,GreenMail
将成为一个由 spring 管理的 bean(可能在一个特殊的配置中,只在测试中加载)但是由于它成为一个 bean,它将能够加载 属性 值并使用 @Value
注释。
在测试执行侦听器中,您将在测试前启动服务器并在测试后停止(或者整个测试 class 如果您需要 - 它具有用于此的“挂钩”)。
另一方面,请确保您 mergeMode = MergeMode.MERGE_WITH_DEFAULTS
作为 @TestExecutionListeners
注释的参数,否则某些默认行为(例如测试中的自动装配、脏上下文(如果有)等)不会工作。
更新 1
关注问题中的更新 1。这不会起作用,因为侦听器本身不是 spring bean,因此您不能在侦听器本身中自动装配或使用 @Value
注释。
您可以尝试遵循
- 使 GreenMail 本身成为一个 bean:
@Configuration
// since you're using @SpringBootTest annotation - it will load properties from src/test/reources/application.properties so you can put spring.mail.port=1234 there
public class MyTestMailConfig {
@Bean
public GreenMail greenMail(@Value(${"spring.mail.port"} int port) {
return new GreenMail(port, ...);
}
}
现在可以将此配置放在src/test/java/<sub-package-of-main-app>/
中,这样在生产中根本不会加载它
- 现在测试执行侦听器只能用于 运行 启动/停止
GreenMail
服务器(据我了解你想在测试前启动它并在测试后停止,否则你根本不需要这些听众 :) )
public class CustomTestExecutionListener implements TestExecutionListener {
@Override
public void beforeTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.start();
}
@Override
public void afterTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.stop();
}
}
另一种选择是自动装配 GreenMail
bean 并使用 JUnit 的 @BeforeEach
和 @AfterEach
方法,但在这种情况下,您必须在不同的测试中复制此逻辑 classes 需要这种行为。监听器允许重用代码。