首页 > 解决方案 > SpringBoot 测试 Docker 数据库

问题描述

所以我有这个 SpringBoot RESTful 服务器连接到 Docker 图像数据库。如果数据库关闭,它会通过响应发送错误消息。我非常想测试它 - 我使用 testcontainers 创建了一个 jUnit+MockMvc 测试,但我仍然无法摆脱“连接到 localhost:5432 被拒绝”。

任何人都知道如何在测试中正确更改应用程序上下文/持久性上下文?

通过在EntityManagerFactory中设置 persistence.xml 的不同属性来解决

内部错误测试.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());
    }
}

持久性.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>

应用程序属性

# 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

标签: javaspringspring-boothibernatejunit

解决方案


推荐阅读