所以我有这个SpringBoot RESTful服务器连接到Docker映像数据库。如果数据库关闭,它将通过响应发送错误消息。我非常想测试它-我使用testcontainers创建了一个jUnit + MockMvc测试,但是我仍然无法摆脱“拒绝连接到localhost:5432”的问题。
任何人都知道如何在测试中正确更改应用程序上下文/持久性上下文吗?
已解决
InternalErrorTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(initializers = {InternalErrorTest.Initializer.class})
@AutoConfigureMockMvc
public class InternalErrorTest {
@Autowired
private MockMvc mockMvc;
@BeforeClass
public static void setTest() {
/**
* still getting "Connection to localhost:5432 refused"
*/
AppInitializer.test = true;
postgreSQLContainer.start();
}
@ClassRule
public static PostgreSQLContainer postgreSQLContainer =
new PostgreSQLContainer("postgres:11.1")
.withDatabaseName("world-db")
.withUsername("world")
.withPassword("world123");
static class Initializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
"hibernate.connection.url=" + postgreSQLContainer.getJdbcUrl(),
"hibernate.connection.username=" + postgreSQLContainer.getUsername(),
"hibernate.connection.password=" + postgreSQLContainer.getPassword()
).applyTo(configurableApplicationContext.getEnvironment());
}
}
@Test
public void shouldReturnINTERNAL_ERRORmsg_whenDatabaseIsDown() throws Exception {
//while (!AppInitializer.isReady) {} //wait for context to set up
//Thread.sleep(5000); //wait for database to set up
//given
String validUrl = "/POL";
int INTERNAL_SERVER_ERROR_STATUS = 500;
String INTERNAL_ERROR_MSG = "INTERNAL_ERROR";
//when, then
mockMvc.perform(get(validUrl)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is(INTERNAL_SERVER_ERROR_STATUS))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("['error message']")
.value(INTERNAL_ERROR_MSG));
}
}
AppInitializer.java
@Component
public class AppInitializer {
@Lazy
@Autowired
CountryRepository countryRepository;
@Autowired
DockerProxy dockerProxy;
private String dockerContainer;
public static boolean isReady = false;
public static boolean test = false;
@PostConstruct
private void init()
throws IOException, InterruptedException, DockerProxyException {
if (!test && executeBashCommand(DockerProxy
.CHECK_DOCKER_PORT_AVAILABILITY_CMD)!=null)
throw new DockerProxyException(DockerProxy
.CHECK_DOCKER_PORT_AVAILABILITY_EXCEPTION);
if (!test && executeBashCommand(DockerProxy
.CHECK_PORT_AVAILABILITY_CMD)!=null)
throw new DockerProxyException(DockerProxy
.CHECK_PORT_AVAILABILITY_EXCEPTION);
try {
if (!test) dockerContainer = executeBashCommand(DockerProxy
.RUN_DOCKER_IMAGE_CMD);
} catch (Exception e) {
throw new DockerProxyException(DockerProxy
.RUN_DOCKER_IMAGE_EXCEPTION);
}
if (!test && executeBashCommand(DockerProxy.getIsContainerRunningCmd(dockerContainer))==null ||
executeBashCommand(DockerProxy.getIsContainerRunningCmd(dockerContainer)).equals("false"))
throw new DockerProxyException(DockerProxy.IS_CONTAINER_RUNNING_EXCEPTION);
if (!test && dockerContainer==null)
throw new DockerProxyException(DockerProxy.DOCKER_ERROR);
}
@PreDestroy
private void destr() throws DockerProxyException {
if (dockerContainer!=null) {
try {
executeBashCommand(DockerProxy
.getStopDockerContainerCmd(dockerContainer));
} catch (Exception e) {
throw new DockerProxyException(DockerProxy
.STOP_DOCKER_CONTAINER_EXCEPTION);
}
try {
executeBashCommand(DockerProxy
.getRemoveDockerContainerCmd(dockerContainer));
} catch (Exception e) {
throw new DockerProxyException(DockerProxy
.REMOVE_DOCKER_CONTAINER_EXCEPTION);
}
}
}
private String executeBashCommand(String command)
throws IOException, InterruptedException {
Process process;
String[] cmdOutput = new String[1];
process = Runtime.getRuntime().exec(command);
StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), (output) -> {
cmdOutput[0] = output;
});
Executors.newSingleThreadExecutor().submit(streamGobbler);
int exitCode = process.waitFor();
if (command.equals(DockerProxy.RUN_DOCKER_IMAGE_CMD) && cmdOutput[0]==null)
return DockerProxy.DOCKER_ERROR;
return cmdOutput[0];
}
private static class StreamGobbler implements Runnable {
private final InputStream inputStream;
private final Consumer<String> consumer;
public StreamGobbler(InputStream inputStream, Consumer<String> consumer) {
this.inputStream = inputStream;
this.consumer = consumer;
}
@Override
public void run() {
new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumer);
}
}
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
isReady = true;
}
}
AppConfig.java
@Lazy
@Configuration
@ComponentScan(basePackages = "com.flairstech.workshop")
@EnableJpaRepositories(bootstrapMode = BootstrapMode.LAZY, basePackages = "com.flairstech.workshop.repositories")
public class AppConfig {
@Bean
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
@Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
return Persistence.
createEntityManagerFactory("workshop_persistence");
}
@Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
@Bean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(transactionManager());
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="workshop_persistence">
<class>com.flairstech.workshop.entities.CountryLanguage</class>
<class>com.flairstech.workshop.entities.Country</class>
<properties>
<property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/world-db"/>
<property name="hibernate.connection.username" value="world"/>
<property name="hibernate.connection.password" value="world123"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL92Dialect"/>
</properties>
</persistence-unit>
</persistence>
application.properties
# DockerProxy-related properties
spring.datasource.hikari.initializationFailTimeout=0
spring.datasource.continue-on-error=true
# Hibernate properties
spring.jpa.open-in-view=false
spring.jpa.hibernate.ddl-auto=validate
spring.h2.console.enabled=true
# Connection Pool properties
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource