契约测例子(基于Nodej、Mocha和assert)

以下是基于 Mocha 框架并结合 Node.js 内置的 assert 模块实现的 Pact 契约测试示例(不使用 Chai):


1. 消费者端测试(User Service)

完全使用 assert 替代 Chai 的 expect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// tests/consumer/orderApiConsumer.spec.js
const { Pact } = require("@pact-foundation/pact");
const assert = require("assert"); // 使用 Node.js 内置 assert
const path = require("path");
const { fetchUserOrders } = require("../../src/client/orderClient");

describe("Order Service Contract Test", () => {
const provider = new Pact({
consumer: "User Service",
provider: "Order Service",
port: 8080,
log: path.resolve(process.cwd(), "logs", "pact.log"),
dir: path.resolve(process.cwd(), "pacts"),
});

before(async () => {
await provider.setup();
});

afterEach(async () => {
await provider.verify();
});

after(async () => {
await provider.finalize();
});

describe("GET /orders/{userId}", () => {
const userId = "123";
const expectedResponse = [
{ id: "1", amount: 100 },
{ id: "2", amount: 200 },
];

beforeEach(async () => {
await provider.addInteraction({
state: "User 123 has orders",
uponReceiving: "a request to get user orders",
withRequest: {
method: "GET",
path: `/orders/${userId}`,
headers: { Accept: "application/json" },
},
willRespondWith: {
status: 200,
headers: { "Content-Type": "application/json" },
body: expectedResponse,
},
});
});

it("returns user orders", async () => {
const response = await fetchUserOrders(userId);
// 使用 assert.deepStrictEqual 替代 Chai 的 expect().to.deep.equal()
assert.deepStrictEqual(response, expectedResponse);
});
});
});

2. 提供者端测试(Order Service)

同样替换为 assert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// tests/provider/orderApiProvider.spec.js
const { Verifier } = require("@pact-foundation/pact");
const assert = require("assert");
const path = require("path");
const { startServer, stopServer } = require("../../src/server");

describe("Order Service Provider Test", () => {
const port = 3000;
let server;

before(async () => {
server = await startServer(port);
});

after(async () => {
await stopServer();
});

it("validates the expectations of User Service", async () => {
const opts = {
provider: "Order Service",
providerBaseUrl: `http://localhost:${port}`,
pactUrls: [path.resolve(process.cwd(), "pacts/user_service-order_service.json")],
};

const result = await new Verifier(opts).verifyProvider();
// 使用 assert.ok + includes 验证结果字符串
assert.ok(result.includes("Pact Verification Complete"));
});
});

3. 关键改动说明

1. 断言语法替换

  • 对象深度比较

    1
    2
    3
    4
    5
    // Chai
    expect(response).to.deep.equal(expectedResponse);

    // assert
    assert.deepStrictEqual(response, expectedResponse);
  • 布尔值验证

    1
    2
    3
    4
    5
    // Chai
    expect(result).to.include("Pact Verification Complete");

    // assert
    assert.ok(result.includes("Pact Verification Complete"));

2. 模块引入

  • 删除 chai 依赖,直接使用 Node.js 内置的 assert 模块:
    1
    const assert = require("assert");

4. 运行测试

1. 调整依赖

无需安装 chai,只需安装 mochapact

1
npm install mocha @pact-foundation/pact --save-dev

2. 执行命令

与之前相同:

1
2
3
4
5
# 消费者端生成契约
npm run test:consumer

# 提供者端验证
npm run test:provider

5. 两种方式的对比

场景 Chai (expect) Node.js assert
语法简洁性 链式语法更直观(如 .to.deep.equal() 函数式调用(如 assert.deepStrictEqual()
依赖管理 需额外安装 chai 无额外依赖
错误信息可读性 错误信息更详细(如差异对比) 错误信息较简单(仅提示断言失败)
适合场景 需要丰富断言语义和复杂验证逻辑 追求轻量化和最小依赖

总结

完全可以使用 Node.js 内置的 assert 替代 Chai,代码改动成本极低,但需注意:

  1. 断言方法差异:例如深度比较需使用 assert.deepStrictEqual
  2. 错误信息简化assert 的错误提示不如 Chai 详细,但可通过自定义错误消息补充:
    1
    assert.deepStrictEqual(a, b, "响应数据与预期不符");
  3. 轻量化优势:减少依赖项,适合对项目简洁性要求高的场景。