使用 Docker 和 Testcontainers 进行数据库集成测试
Using Docker and Testcontainers for database integration testing
经过一番研究后,我认为 Docker 容器可能非常适合使用 test containers 进行数据库集成测试,因为这只需要一个 Docker 容器运行创建一个数据库映像,再现将针对其执行测试的数据库架构。该实例可以 运行 在每个开发人员的本地机器上,或者更好的是,一个实例可以由多个开发人员共享:自动化测试可以配置为从 Docker 中的同一图像启动数据库实例] 如果需要,使用 @Rule 注释为每个测试方法。
在尝试为 Windows 7 安装 Docker 时,我不断收到以下似乎与 VirtualBox 相关的错误:
docker Error creating machine: Error in driver during machine creation: Unable to start the VM: VBoxManage.exe startvm default --type headless failed:
Result code: E_FAIL (0x80004005)
Component: MachineWrap
Interface: IMachine
我将分享我是如何解决这个问题的,以帮助可能 运行 遇到同样问题的其他人。
为了解决问题中描述的问题,我按照以下步骤操作:
#1。安装 VMware workstation player#
#2。 运行一个LinuxUbuntu虚拟机#
使用 Ubuntu website 中的 iso 文件,运行 VMware 播放器上的 Ubuntu 设备
#3。在 Ubuntu VM#
上安装 Docker
$ sudo apt-get install docker.io
#4。 Post-安装步骤#
###4.1。创建 docker 组并添加您的用户###
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
###4.2。注销并重新登录##
以便重新评估您的群组成员资格
###4.3。验证您可以 运行 docker 命令而无需 sudo###
$ docker run hello-world
#5。下载数据库镜像#
在此示例中,它是 Oracle 11g:
$ docker pull wnameless/oracle-xe-11g-r2
#6。 运行 数据库图像#
(如果您打算从testcontainers连接到Oracle数据库实例,则不需要此步骤)
$ docker run -d -p 49161:1521 wnameless/oracle-xe-11g-r2
进一步details/options参考docker hub:
#7。连接到数据库#
使用以下设置:
hostname: localhost
port: 49161
sid: xe
service name: xe
username: system
password: oracle
要从主机连接,请使用 VM 的 IP 地址而不是 localhost
。 运行 ifconfig
在来宾 Ubuntu VM 上获取 IP 地址
#8。配置Docker远程访问#
配置 Docker 守护程序侦听连接的位置(仅当 Docker 需要远程访问时才需要,即不是来自访客 Ubuntu 系统),默认情况下 Docker 守护进程监听 unix 套接字(本地连接)
###8.1。创建配置文件###
创建 /etc/systemd/system/docker.service.d/override.conf
文件,其内容覆盖默认文件 (ExecStart=/usr/bin/dockerd -H fd://
):
# /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376
要创建此文件,您可以运行以下 unix 命令:
printf "#
/etc/systemd/system/docker.service.d/override.conf\n[Service]\nExecStart=\nExecStart=/usr/bin/dockerd
-H fd:// -H tcp://0.0.0.0:2376" > /etc/systemd/system/docker.service.d/override.conf
###8.2。重新启动 Docker###
保存此文件后,通过 运行ning 重新加载配置:
systemctl daemon-reload
然后通过 运行ning 重启 Docker:
systemctl restart docker.service
###8.3。检查你的 Docker 守护进程###
重新启动docker服务后,您可以在任一输出中看到端口号:
systemctl status docker.service
(您应该看到类似的内容:/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376
)
或
sudo netstat -tunlp | grep dock
(您应该会看到如下内容:tcp6 0 0 :::2376 :::* LISTEN 121686/dockerd
)
###8.4。有用的资源###
How do I enable the remote API for dockerd
How to detect a docker daemon port
Configure where the docker daemon listens for connections
#9。设置Docker主机环境变量#
仅当您计划使用 testcontainers API(例如来自 Junit 测试)从托管 Ubuntu VM 的 OS 远程连接到数据库容器时,才需要执行此步骤( docker dameon 运行正在 ubuntu VM 上运行)
定义环境变量:DOCKER_HOST = tcp://<Ubuntu machine's IP address>:2376
。请注意,主机名是 ubuntu 个虚拟机。如果未定义此环境变量,testcontainers API (OracleContainer oracleContainer = new OracleContainer("wnameless/oracle-xe-11g");) 将期望 Docker 守护程序在本地主机上 运行ning (请参阅下面的代码片段)
#10。使用测试中的数据库容器 class#
使用测试容器 API,Junit 测试可以从 Ubuntu 的 VM 上的 Docker 映像启动数据库实例,对其执行查询,并最终将其关闭
###Junit 测试class###
package com.xxx.yyy.repository;
import static org.junit.Assert.assertEquals;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.time.LocalDateTime;
import java.util.concurrent.TimeoutException;
import org.junit.ClassRule;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.testcontainers.containers.OracleContainer;
@TestInstance(Lifecycle.PER_CLASS)
public class MyRepositoryIT {
@ClassRule
public OracleContainer oracleContainer;
@BeforeAll
public void setup() throws TimeoutException {
String dockerHost = System.getenv("DOCKER_HOST");
System.out.println("dockerHost: @" + dockerHost + "@");
System.out.println("Starting Oracle Container... (" + LocalDateTime.now() + ")");
oracleContainer = new OracleContainer("wnameless/oracle-xe-11g");
oracleContainer.start();
System.out.println("Oracle Container started. (" + LocalDateTime.now() + ")");
}
@AfterAll
public void tearDown() {
System.out.println("Stopping Oracle Container... (" + LocalDateTime.now() + ")");
oracleContainer.stop();
System.out.println("Oracle Container stopped. (" + LocalDateTime.now() + ")");
}
@Test
public void whenSelectQueryExecuted_thenResulstsReturned() throws Exception {
String jdbcUrl = oracleContainer.getJdbcUrl();
String username = oracleContainer.getUsername();
String password = oracleContainer.getPassword();
Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
ResultSet resultSet = conn.createStatement().executeQuery("SELECT 1 FROM DUAL");
resultSet.next();
int result = resultSet.getInt(1);
assertEquals(1, result);
}
}
###Maven 依赖项###
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxx.yyy</groupId>
<artifactId>docker-testcontainers</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring.version>5.1.3.RELEASE</spring.version>
<testcontainers.version>1.10.2</testcontainers.version>
<junit-engine.version>5.3.2</junit-engine.version>
<junit-launcher.version>1.3.2</junit-launcher.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>oracle-xe</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>12.1.0.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit-launcher.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
#杂记#
###有用的 docker 命令###
- 列出图片:
docker images
- 列出所有容器:
docker ps -a
- 启动一个容器:
docker start [container id]
- 列出已启动的容器:
docker ps
- 查看启动容器的日志:
docker logs [container id]
###参考文献###
Further details about Post-install steps
Using an Oracle image within Docker
Database Testing With TestContainers
###关于 Oracle 12c 镜像###
我试过 Oracle 12c 映像(sath89/oracle-12c
来自:https://hub.docker.com/r/sath89/oracle-12c
)
$ docker run -d -p 8080:8080 -p 1521:1521 --name oracle-db-12c sath89/oracle-12c
但从 testcontainers 启动似乎很慢,最终(大约 4 分钟后)抛出了以下异常:
java.sql.SQLRecoverableException: ORA-01033: ORACLE initialization or shutdown in progress.
如果 12c 映像是从 docker 主机本身启动的(即 Ubuntu),它会成功启动。
经过一番研究后,我认为 Docker 容器可能非常适合使用 test containers 进行数据库集成测试,因为这只需要一个 Docker 容器运行创建一个数据库映像,再现将针对其执行测试的数据库架构。该实例可以 运行 在每个开发人员的本地机器上,或者更好的是,一个实例可以由多个开发人员共享:自动化测试可以配置为从 Docker 中的同一图像启动数据库实例] 如果需要,使用 @Rule 注释为每个测试方法。
在尝试为 Windows 7 安装 Docker 时,我不断收到以下似乎与 VirtualBox 相关的错误:
docker Error creating machine: Error in driver during machine creation: Unable to start the VM: VBoxManage.exe startvm default --type headless failed:
Result code: E_FAIL (0x80004005)
Component: MachineWrap
Interface: IMachine
我将分享我是如何解决这个问题的,以帮助可能 运行 遇到同样问题的其他人。
为了解决问题中描述的问题,我按照以下步骤操作:
#1。安装 VMware workstation player#
#2。 运行一个LinuxUbuntu虚拟机#
使用 Ubuntu website 中的 iso 文件,运行 VMware 播放器上的 Ubuntu 设备
#3。在 Ubuntu VM#
上安装 Docker$ sudo apt-get install docker.io
#4。 Post-安装步骤#
###4.1。创建 docker 组并添加您的用户###
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
###4.2。注销并重新登录## 以便重新评估您的群组成员资格
###4.3。验证您可以 运行 docker 命令而无需 sudo###
$ docker run hello-world
#5。下载数据库镜像#
在此示例中,它是 Oracle 11g:
$ docker pull wnameless/oracle-xe-11g-r2
#6。 运行 数据库图像#
(如果您打算从testcontainers连接到Oracle数据库实例,则不需要此步骤)
$ docker run -d -p 49161:1521 wnameless/oracle-xe-11g-r2
进一步details/options参考docker hub:
#7。连接到数据库#
使用以下设置:
hostname: localhost
port: 49161
sid: xe
service name: xe
username: system
password: oracle
要从主机连接,请使用 VM 的 IP 地址而不是 localhost
。 运行 ifconfig
在来宾 Ubuntu VM 上获取 IP 地址
#8。配置Docker远程访问#
配置 Docker 守护程序侦听连接的位置(仅当 Docker 需要远程访问时才需要,即不是来自访客 Ubuntu 系统),默认情况下 Docker 守护进程监听 unix 套接字(本地连接)
###8.1。创建配置文件###
创建 /etc/systemd/system/docker.service.d/override.conf
文件,其内容覆盖默认文件 (ExecStart=/usr/bin/dockerd -H fd://
):
# /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376
要创建此文件,您可以运行以下 unix 命令:
printf "# /etc/systemd/system/docker.service.d/override.conf\n[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376" > /etc/systemd/system/docker.service.d/override.conf
###8.2。重新启动 Docker###
保存此文件后,通过 运行ning 重新加载配置:
systemctl daemon-reload
然后通过 运行ning 重启 Docker:
systemctl restart docker.service
###8.3。检查你的 Docker 守护进程###
重新启动docker服务后,您可以在任一输出中看到端口号:
systemctl status docker.service
(您应该看到类似的内容:/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376
)
或
sudo netstat -tunlp | grep dock
(您应该会看到如下内容:tcp6 0 0 :::2376 :::* LISTEN 121686/dockerd
)
###8.4。有用的资源###
How do I enable the remote API for dockerd
How to detect a docker daemon port
Configure where the docker daemon listens for connections
#9。设置Docker主机环境变量#
仅当您计划使用 testcontainers API(例如来自 Junit 测试)从托管 Ubuntu VM 的 OS 远程连接到数据库容器时,才需要执行此步骤( docker dameon 运行正在 ubuntu VM 上运行)
定义环境变量:DOCKER_HOST = tcp://<Ubuntu machine's IP address>:2376
。请注意,主机名是 ubuntu 个虚拟机。如果未定义此环境变量,testcontainers API (OracleContainer oracleContainer = new OracleContainer("wnameless/oracle-xe-11g");) 将期望 Docker 守护程序在本地主机上 运行ning (请参阅下面的代码片段)
#10。使用测试中的数据库容器 class#
使用测试容器 API,Junit 测试可以从 Ubuntu 的 VM 上的 Docker 映像启动数据库实例,对其执行查询,并最终将其关闭
###Junit 测试class###
package com.xxx.yyy.repository;
import static org.junit.Assert.assertEquals;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.time.LocalDateTime;
import java.util.concurrent.TimeoutException;
import org.junit.ClassRule;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.testcontainers.containers.OracleContainer;
@TestInstance(Lifecycle.PER_CLASS)
public class MyRepositoryIT {
@ClassRule
public OracleContainer oracleContainer;
@BeforeAll
public void setup() throws TimeoutException {
String dockerHost = System.getenv("DOCKER_HOST");
System.out.println("dockerHost: @" + dockerHost + "@");
System.out.println("Starting Oracle Container... (" + LocalDateTime.now() + ")");
oracleContainer = new OracleContainer("wnameless/oracle-xe-11g");
oracleContainer.start();
System.out.println("Oracle Container started. (" + LocalDateTime.now() + ")");
}
@AfterAll
public void tearDown() {
System.out.println("Stopping Oracle Container... (" + LocalDateTime.now() + ")");
oracleContainer.stop();
System.out.println("Oracle Container stopped. (" + LocalDateTime.now() + ")");
}
@Test
public void whenSelectQueryExecuted_thenResulstsReturned() throws Exception {
String jdbcUrl = oracleContainer.getJdbcUrl();
String username = oracleContainer.getUsername();
String password = oracleContainer.getPassword();
Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
ResultSet resultSet = conn.createStatement().executeQuery("SELECT 1 FROM DUAL");
resultSet.next();
int result = resultSet.getInt(1);
assertEquals(1, result);
}
}
###Maven 依赖项###
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxx.yyy</groupId>
<artifactId>docker-testcontainers</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring.version>5.1.3.RELEASE</spring.version>
<testcontainers.version>1.10.2</testcontainers.version>
<junit-engine.version>5.3.2</junit-engine.version>
<junit-launcher.version>1.3.2</junit-launcher.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>oracle-xe</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>12.1.0.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit-launcher.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
#杂记#
###有用的 docker 命令###
- 列出图片:
docker images
- 列出所有容器:
docker ps -a
- 启动一个容器:
docker start [container id]
- 列出已启动的容器:
docker ps
- 查看启动容器的日志:
docker logs [container id]
###参考文献###
Further details about Post-install steps
Using an Oracle image within Docker
Database Testing With TestContainers
###关于 Oracle 12c 镜像###
我试过 Oracle 12c 映像(sath89/oracle-12c
来自:https://hub.docker.com/r/sath89/oracle-12c
)
$ docker run -d -p 8080:8080 -p 1521:1521 --name oracle-db-12c sath89/oracle-12c
但从 testcontainers 启动似乎很慢,最终(大约 4 分钟后)抛出了以下异常:
java.sql.SQLRecoverableException: ORA-01033: ORACLE initialization or shutdown in progress.
如果 12c 映像是从 docker 主机本身启动的(即 Ubuntu),它会成功启动。