【发布时间】:2022-02-23 16:49:04
【问题描述】:
我正在开发一个调用外部 API 的系统,其中一些由我的公司拥有,而另一些则不是。
我的系统由一个 HTTP 接口组成,该接口接受订单并将其发布到消息队列中以运行操作链。我的系统由 3 个 NodeJS 进程(1 个用于 HTTP,2 个消息队列消费者)、2 个数据库和一个消息队列组成。
当我开发我的应用程序时,很难测试我的系统涵盖的所有场景(即使我有单元测试)。为了确保所有组件协同工作,我正在使用 Gherkin 语言和 cucumber js 编写规范。
为了测试系统,我想和部署环境一样接近,所以我用 docker-compose 启动了我所有的系统,包括数据库、NodeJS 进程和消息队列。系统的所有组件都通过 docker-compose 配置中定义的 docker 网络进行通信。
问题是我无法确保所有外部 API 都处于准备好接受我的请求的正确状态,并且它们会以对我的测试步骤感兴趣的方式响应。
所以,我考虑为我的每个依赖项使用 Mock 服务器并发现了 pact.io。据我了解,Pact 允许我编写合约并启动一个模拟服务器,这样我的系统就可以对模拟服务器运行 HTTP 请求。 Pact 还允许我将合同提供给服务提供商,以便它还可以针对真实应用运行合同,看看它是否真的有效。
我看到了 javascript 中的示例,我能够启动模拟服务,为其提供交互,验证交互并关闭模拟服务。 (JS with mocha example)
我的问题是我希望我的系统与生产系统一样接近,因此我希望它通过我的 docker 网络访问 Pact 模拟服务。我看到了一个 Pact CLI docker 映像来运行 pact 模拟服务 (Pact CLI docker image),但是一旦我的模拟服务器被 docker 化,我就失去了使用 JS 包装器对add new interactions 的控制。
另外,我不想编写协议文件,我想在测试运行时添加交互,否则我将声明测试数据两次(一次在黄瓜测试场景中,一次在协议文件中)。
我的问题是:
有没有办法将 JS 包装器绑定到现有的模拟服务,一个 dockerize 服务? 使用 docker pact 镜像时,有没有办法在运行时添加交互? 因为我只需要一个模拟服务,所以 pact 是正确的工具吗?
编辑
我只是创建了一个沙盒环境来看看 NodeJS 包装器可以做什么。看来您可以使用 docker 创建一个模拟服务,并通过 NodeJS 包装器对其进行控制。
# Starts the docker container
docker run -dit \
--rm \
--name pact-mock-service \
-p 1234:1234 \
-v <YOUR_PATH>/docker_pacts/:/tmp/pacts \
pactfoundation/pact-cli:latest \
mock-service \
-p 1234 \
--host 0.0.0.0 \
--pact-dir /tmp/pacts
const {Pact, MockService, } = require('@pact-foundation/pact')
const axios = require('axios')
const pact = new Pact({
consumer: "my client",
provider: "some provider",
// Those two are ignored since we override the inner mock service
port: 1234,
host: 'localhost'
})
const mockService = new MockService(
// You need to duplicate those data, normally they are passed
// by the pact object when calling `pact.setup()`.
'my client',
'provider',
// the port and host to the docker container
1234,
'localhost'
)
pact.mockService = mockService
async function run () {
await pact.addInteraction({
state: "some data is created",
withRequest: {
method: "GET",
path: "/hello"
},
willRespondWith: {
status: 200,
body: {
hello: 'hello world'
}
},
uponReceiving: ''
})
const response = await axios.get('http://localhost:1234/hello')
console.log(response.data) // { "hello": "hello world" }
}
run().catch(console.error)
编辑 2
我可能会按照 Matthew Fellows 的回答,使用 Pact 模拟我系统的外部交互的某种单元测试来测试我的系统。
【问题讨论】:
标签: cucumberjs pact