“干净”的解决方案是使用ApplicationContextInitializer。
请参阅this answer 来回答类似问题。
另请参阅 this github issue 提出类似问题。
使用经过清理以保护版权所有者的真实示例总结上述帖子(我有一个 REST 端点,它使用 @Autowired DataSource 需要使用动态属性来知道哪个端口在-内存 MySQL 数据库正在使用):
- 您的测试必须声明初始化程序(请参阅下面的
@ContextConfiguration 行)。
// standard spring-boot test stuff
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("local")
@ContextConfiguration(
classes = Application.class,
// declare the initializer to use
initializers = SpringTestDatabaseInitializer.class)
// use random management port as well so we don't conflict with other running tests
@TestPropertySource(properties = {"management.port=0"})
public class SomeSprintTest {
@LocalServerPort
private int randomLocalPort;
@Value("${local.management.port}")
private int randomManagementPort;
@Test
public void testThatDoesSomethingUseful() {
// now ping your service that talks to the dynamic resource
}
}
- 您的初始化程序需要将动态属性添加到您的环境中。不要忘记为需要运行的任何清理添加关闭挂钩。以下是使用自定义
DatabaseObject 类设置内存数据库的示例。
public class SpringTestDatabaseInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final int INITIAL_PORT = 0; // bind to an ephemeral port
private static final String DB_USERNAME = "username";
private static final String DB_PASSWORD = "password-to-use";
private static final String DB_SCHEMA_NAME = "default-schema";
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
DatabaseObject databaseObject = new InMemoryDatabaseObject(INITIAL_PORT, DB_USERNAME, DB_PASSWORD, DB_SCHEMA_NAME);
registerShutdownHook(databaseObject);
int databasePort = startDatabase(databaseObject);
addDatabasePropertiesToEnvironment(applicationContext, databasePort);
}
private static void addDatabasePropertiesToEnvironment(ConfigurableApplicationContext applicationContext, int databasePort) {
String url = String.format("jdbc:mysql://localhost:%s/%s", databasePort, DB_SCHEMA_NAME);
System.out.println("Adding db props to environment for url: " + url);
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"db.port=" + databasePort,
"db.schema=" + DB_SCHEMA_NAME,
"db.url=" + url,
"db.username=" + DB_USERNAME,
"db.password=" + DB_PASSWORD);
}
private static int startDatabase(DatabaseObject database) {
try {
database.start();
return database.getBoundPort();
} catch (Exception e) {
throw new IllegalStateException("Failed to start database", e);
}
}
private static void registerShutdownHook(DatabaseObject databaseObject) {
Runnable shutdownTask = () -> {
try {
int boundPort = databaseObject.getBoundPort();
System.out.println("Shutting down database at port: " + boundPort);
databaseObject.stop();
} catch (Exception e) {
// nothing to do here
}
};
Thread shutdownThread = new Thread(shutdownTask, "Database Shutdown Thread");
Runtime.getRuntime().addShutdownHook(shutdownThread);
}
}
当我查看日志时,它表明我的两个测试都使用了这个初始化器类,它们使用了同一个对象(initialize 方法只被调用一次,关闭挂钩也是如此)。因此它会启动一个数据库,让它一直运行直到两个测试都完成,然后关闭数据库。