Pact:契約測試

契約測試 (Contract Testing)

微服務讓開發者可將系統拆解為許多小部份來實作再做組裝成一個大的應用程式,而拆解成小的服務,就必須確保這些服務可經由特定介面來做通訊。

這樣的介面必定會兩方使用者-提供者 (provider) 與消費者 (consumer),provider 提供資料,而 consumer 處理資料。在 web 的世界裡,provider 可能是 API,而 consumer 是呼叫 API 的 UI 或另一支 API 等,並且介面就稱為契約 (contract)。

這樣的契約測試 (contract test) 確保了介面的穩定性,當介面有變時能及早發現及早治療。

消費者驅動的契約測試 (Consumer-Driven Contract Test)

消費者驅動的契約測試 (consumer-driven contract test) 是指由 consumer 發起契約的實現與驗證。

可以想像成是這樣的流程…

  1. 有一份契約文件規範了 provider 必須要提供和 consumer 所能接受的資料格式。
  2. consumer 提出請求,希望能得到 provider 的回應。
  3. provider 提供資料,consumer 根據回傳的資料驗證是否正確。

Pact

Pact 是用於實作契約測試的框架,在這裡用 JavaScript 的版本-Pact JS 來做 pact boker。

加入互動來描述這個 consumer 提出請求與 provider 提供資料的過程…

範例

範例如下,測試指定的 API (provider),接著 UI (consumer) 收到資料後來驗證是否符合預期。

import { Pact } from '@pact-foundation/pact';

const provider = new Pact({ /* define pact object, ex: provider, consumer, port... */});

describe('Get List API', () => {
    beforeAll(() => provider.setup());

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

    afterAll(async () => provider.finalize());

    test('Test XXX API', async () => {
        const GET_LIST_API = 'https://sample.com/get_list';
        await provider.addInteraction({
            // 預期要對 provider 提出的請求與 payload
            withRequest: {
                method: 'GET',
                path: GET_LIST_API,
                headers: { /* ... */ },
            }
            // 預期 provider 回傳的結果與 payload
            willRespondWith: {
                status: 200,
                headers: { 'Content-Type': 'application/json' },
                body: {
                    code: 0,
                    status: 1,
                    message: 'success',
                }
            }
        });

        // consumer 對 pact 的 mock server 提出請求
        await SendRequest(getApi(GET_LIST_API), requestData)
            .then((data) => {
                // 驗證 provider 回傳資料是否符合預期
                expect(data).toHaveProperty('code');
                expect(data).toHaveProperty('status');
                expect(data).toHaveProperty('message');
            });
    });
});

參考資料


comments powered by Disqus