Docker Compose进行集成测试

阅读量406152

发布时间 : 2023-07-12 13:54:18

集成测试通常是一项困难的活动,特别是在涉及到分布式系统时。即便正在构建单体应用,也可能需要启动数据库,来进行集成测试。这种事情在早期很容易做到,但随着代码库的增加,难度将呈指数级增长。值得庆幸的是,Docker Compose使我们能够在运行 Docker 的任何环境中,进行集成测试。

开始

假设从一个单体体制开始,拥有一个服务和一个数据库。你可以像 1999 年那样,从源代码构建应用服务和数据库;或使用 brew install 解决所有依赖关系。但最终你的系统看起来是这样的:

待测试端点是/create,它做的全部事情是在数据库中存储一些数据。看起来非常简单。因此,可以编写如下 Bash 脚本 – CURL 端点;然后查询数据库(退出码 0 代表成功;退出码 1 代表失败)。该脚本很简单,但最重要的是它有效。

curl http://localhost:8000/create
COUNT=`mysql –user=”$user” –password=”$password” –database=”$database”\
–execute=”SELECT COUNT(*) FROM table_name;”`if[[$COUNT -ne 1]];thenexit1fi

但是有很多隐藏的依赖项:

  1. 必须安装和运行数据库
  2. 必须安装单体应用框架
  3. 必须运行单体应用
  4. 需要 PATH 中有 CURL 的操作系统
  5. 根据测试,数据库中的任何数据都可能导致测试结果不准确。

假设在 Bash 脚本中添加一行,重置数据。

mysql –user=”$user” –password=”$password” –database=”$database” \
–execute=”TRUNCATE table table_name”
curl http://localhost:8000/create
COUNT = `mysql –user=”$user” –password=”$password” –database=”$database”\
–execute=”SELECT COUNT(*) FROM table_name;”`
if [[ $COUNT -ne 1 ]]; then
exit 1
fi

这样做可以消除最后一个隐藏依赖项(数据库中存在数据),但也带来非常严重的副作用,因为本地开发数据库与测试数据库共享。因此,每次运行集成测试,都会丢失全部开发数据 。这似乎显而易见,但实际上这种体制仍然存在。然而不一定非要这样做。从此处开始,我将通过一个构建在 Docker Compose 上的示例,解决上面列出的所有问题。在本例中,将使用 Node 作为应用程序框架,使用 RethinkDB 作为数据库,但是你也可以选择其它技术栈。

制定策略

我们从Martin Fowler 的微服务测试手册中学习集成测试。我们将在被测试的系统外部启动一个容器,使容器运行一些测试,然后检查测试容器的 run 命令的退出代码。

为清晰起见,下面列出文件结构,因为该项目中有多个Dockerfile

integration-test/
Dockerfile
index.js
package.json
test.sh
docker-compose.yml
index.js
package.json
Dockerfile

接下来走查集成测试的每个组件。

临时数据库

有时丢弃所有数据是好事,在运行测试时,丢弃数据是必要的。使用 Docker compose 实现这一点非常容易,只需启动数据库,无需挂载数据卷。这意味着当销毁容器时,数据也随之消失。还意味着如果不销毁容器,那么可以进入容器内部,对数据库运行查询,进行调试。下面是一个示例 Docker Compose 文件,它只启动一个临时数据库(RethinkDB)。

integration-test/docker-compose.yml

version:’2’services:rethinkdb:image:rethinkdbexpose:- “28015”

记住这个概念,因为我们很快就会用到它。

应用程序容器

下一步是容器化将要测试的应用程序。需要构建/运行应用程序,连接数据库,以及暴露用于测试的端口。

Dockerfile

FROM mhart/alpine-node
WORKDIR /service
COPY package.json .
RUN npm install
COPY index.js .

integration-test/docker-compose.yml

version:’2’services:my-service:build:..command:npm startlinks:- rethinkdbports:- “8080:8080″rethinkdb:image:rethinkdbexpose:- “28015”

此时,可以使用docker-compose up检查服务,以及访问http://localhost:8080(只要你拥有服务器,并且线路已连接)。

集成测试容器

现在,我们已拥有数据库和应用程序,接下来构建测试容器。该容器需要向my-service上的/create端点发送 POST 请求,并且检查数据库中的变更。为实现这一点,这里使用tape和request-promise检查端点。

integration-test/index.js

importtestfrom’tape’;importrequestPromisefrom’request-promise’;constbefore=test;constafter=test;constbeforeEach=()=>{/*test setup*/};constafterEach=()=>{/*test cleanup*/};before(‘before’,(t)=>{/*one time setup*/});test(‘POST /create’,(t)=>{beforeEach().then(()=>(requestPromise({method:’POST’,// yes! we can use the service name in the docker-compose.yml file
uri:’http://my-service:8080/create’,body:{thing:’this thing’,},}))).then((response)=>{// inspect the response
t.equal(response.statusCode,200,’statusCode: 200′);}).then(()=>(// inspect the database
rethinkdb.table(‘table_name’).filter({thing:’this thing’,}).count().run(connection).then((value)=>{t.equal(value,1,’have data’);}))).catch((error)=>t.fail(error)).then(()=>afterEach()).then(()=>t.end());});after(‘after’,(t)=>{/*one time setup*/});

测试 Dockerfile 看起来与应用程序 Dockerfile 相同。

integration-test/Dockerfile

FROM mhart/alpine-node
WORKDIR /integration
COPY package.json .
RUN npm install
COPY index.js .

现在,将测试应用程序添加到docker-compose.yml文件。

integration-test/docker-compose.yml

version:’2’services:integration-tester:build:.links:- my-servicemy-service:build:..command:npm startlinks:- rethinkdbports:- “8080:8080″rethinkdb:image:rethinkdbexpose:- “28015”

这里是很酷的部分,当运行docker-compose up时,将发生如下事情

  1. 构建my-serviceintegration-tester容器
  2. 连接及运行my-serviceintegration-testerrethinkdb容器
  3. integration-tester运行所有测试,直到停止
  4. integration-tester停止后,docker-compose关闭所有容器

这正是需要在 CI 中运行的集成测试。到目前为止,我们尚未检查integration-tester容器的退出码,接下来马上讲述。

将所有东西结合起来

在所有自动化工作就绪后,我们需要将所有东西结合起来,并且在测试完成后,执行清理工作。为此,我们使用docker wait阻塞脚本,获取测试的退出码。我们使用该退出码输出消息(通过/失败),并且使用相同的退出码退出主脚本。这很有用因为大多数(并非全部)CI 环境使用退出码确定测试成功与否。我们还将获取测试容器的日志,并且将它们打印出来,以便在测试失败时提供上下文。下面是一个(极其冗长的)脚本,它完成我们在本地或 CI 中运行集成测试所需的一切。

integration-test/test.sh

# define some colors to use for output
RED=’\033[0;31m’
GREEN=’\033[0;32m’
NC=’\033[0m’
# kill and remove any running containers
cleanup () {
docker-compose -p ci kill
docker-compose -p ci rm -f –all
}
# catch unexpected failures, do cleanup and output an error message
trap ‘cleanup ; printf “${RED}Tests Failed For Unexpected Reasons${NC}\n”‘\
HUP INT QUIT PIPE TERM
# build and run the composed services
docker-compose -p ci build && docker-compose -p ci up -d
if [ $? -ne 0 ] ; then
printf “${RED}Docker Compose Failed${NC}\n”
exit -1
fi
# wait for the test service to complete and grab the exit code
TEST_EXIT_CODE=`docker wait ci_integration-tester_1`
# output the logs for the test (for clarity)
docker logs ci_integration-tester_1
# inspect the output of the test and display respective message
if [ -z ${TEST_EXIT_CODE+x} ] || [ “$TEST_EXIT_CODE” -ne 0 ] ; then
printf “${RED}Tests Failed${NC} – Exit Code: $TEST_EXIT_CODE\n”
else
printf “${GREEN}Tests Passed${NC}\n”
fi
# call the cleanup fuction
cleanup
# exit the script with the same code as the test service code
exit $TEST_EXIT_CODE

示例

如欲获得完整示例,请查看auth-service。想要看到它的实际效果,你需要做:

git clone https://github.com/hharnisc/auth-service.git
cd auth-service
npm test

对于更复杂的示例(多层微服务),请查看login-service。

git clone https://github.com/hharnisc/login-service.git
cd login-service
npm test

总结

这种方式在实践中效果很好,我已经使用该方式为一些微服务执行集成测试。每当我在 CI 中遇到失败时,同样的 Bug 肯定可以在本地复现。我遇到的最大问题是,因为应用程序没有完全启动,而导致的测试失败。为解决该问题,我在应用程序上实现一个/healthAPI 端点,并且在测试的before块内部添加重试。自从修复该问题后,再没遇到其它古怪的问题,并且一直使用该方式在 CI 中运行集成测试。这真的很有用,并且已经捕获一些可能在部署过程中出现的实际 Bug,我希望你也能发现它有用。

Read Also

  1. https://docs.docker.com/compose/startup-order/
  2. https://docs.docker.com/compose/networking/#link-containers
  3. https://docs.docker.com/compose/compose-file/#depends_on
  4. https://docs.docker.com/compose/compose-file/#healthcheck
  5. https://docs.docker.com/engine/

 

本文由星阑科技原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/289667

安全客 - 有思想的安全新媒体

分享到:微信
+125赞
收藏
星阑科技
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66