无法让 Cypress 和 Pact 一起工作

0 javascript testing contract pact cypress

我已经有一个工作项目,几乎没有通过赛普拉斯测试。现在我正在尝试使用 Cypress + Pact 添加合约测试

在开发人员控制台中,我可以看到应用程序正在调用/api/v1/document-service,但我得到:

协议验证失败 - 预期交互与实际不符。

部分日志:

W, [2021-07-20T12:49:37.157389 #34805]  WARN -- : Verifying - actual interactions do not match expected interactions.

Missing requests:
    POST /api/v1/document-service

W, [2021-07-20T12:49:37.157489 #34805]  WARN -- : Missing requests:
    POST /api/v1/document-service
Run Code Online (Sandbox Code Playgroud)

我在用着:

  cypress: 7.5.0
  @pact-foundation/pact: 9.16.0
Run Code Online (Sandbox Code Playgroud)

我已完成的步骤:

  1. 添加了 cypress 插件(https://github.com/pactflow/example-consumer-cypress/blob/master/cypress/plugins/cypress-pact.js

  2. 添加了命令(https://github.com/pactflow/example-consumer-cypress/blob/master/cypress/support/commands.js

  3. 将配置添加到 cypress.json ( https://github.com/pactflow/example-consumer-cypress/blob/master/cypress.json ) - 如果我不想与真实服务器交互,则不确定要添加到 baseUrl 中的内容。

  4. 添加测试:

    let server;
    
    describe('Simple', () => {
        before(() => {
            cy.mockServer({
                consumer: 'example-cypress-consumer',
                provider: 'pactflow-example-provider',
            }).then(opts => {
                cy.log(opts)
                server = opts
            })
        });
    
        beforeEach(() => {
            cy.fakeLogin()
    
            cy.addMockRoute({
                server,
                as: 'products',
                state: 'products exist',
                uponReceiving: 'a request to all products',
                withRequest: {
                    method: 'POST',
                    path: '/api/v1/document-service',
                },
                willRespondWith: {
                    status: 200,
                    body: {
                        data: {
                            collections: [
                                {
                                    id: '954',
                                    name: 'paystubs',
                                },
                                {
                                    id: '1607',
                                    name: 'mystubs',
                                },
                            ],
                        },
                    },
                },
            });
        });
        it('is ok?', () => {
            cy.visit('/new/experiments/FirstProject/collections');
        });
    })
    
    Run Code Online (Sandbox Code Playgroud)

尝试使用已弃用的cy.server()/cy.route()和 new cy.intercept(),但仍然验证失败。

Bet*_*rie 6

在 Pactflow,我们花了大约 6 个月的时间一起使用 Cypress 和 Pact,使用 Pact 模拟服务根据我们的 Cypress 测试生成 Pact,如 https://pactflow.io/blog/cypress-pact-front-end-testing中的建议-有信心/

出于以下原因,我们本周决定改变我们的方法。

  1. 我们的 Cypress 测试比我们的单元测试慢得多(15 分钟以上),因此当我们从 Cypress 测试与单元测试生成协议时,生成协议并验证它需要更长的时间。
  2. 我们的 Cypress 测试可能会因与 Pact 无关的原因而失败(例如,布局更改、构建节点上的内存消耗问题、UI 库升级等),这会阻止生成 Pact。
  3. 赛普拉斯拦截()和模拟服务不能很好地协同工作。任何没有 Cypress 显式拦截()的请求最终都会到达模拟服务,并且每次页面使用新端点时,请求都会到达模拟服务,然后整个测试就会失败,即使该端点也是如此该特定测试不需要。
  4. 当 Cypress 测试失败时,Cypress 库提供了非常好的调试功能。当它因与 Pact 有关的原因而失败时,目前很难确定原因,因为该信息不会显示在浏览器中(这可能会得到改进,但是,它不会缓解已经列出的问题)。

我们的新方法是从我们的单元测试(而不是我们的 Cypress 测试)生成我们的 Pact,并使用剥离 Pact 匹配器的函数在单元和 Cypress 测试之间共享固定装置。例如。

const SOME_INTERACTION = {
  state: 'some state',
  uponReceiving: "some request",
  withRequest: {
    method: "GET",
    path: "/something"
  },
  willRespondWith: {
    status: 200,
    body: {
      something: like("hello")
    }
  }
}

Run Code Online (Sandbox Code Playgroud)

单元测试

describe('Something', () => {
  let someProviderMockService
  beforeAll(() => {
    someProviderMockService = new Pact({
      consumer: 'some-consumer',
      provider: 'some-provider'
    })
    return someProviderMockService.setup()
  })
  afterAll(() => someProviderMockService.finalize())
  afterEach(() => someProviderMockService.verify())

  describe('loadSomething', () => {
    beforeEach(() => {
      return someProviderMockService.addInteraction(SOME_INTERACTION)
    })

    it('returns something', async () => {
      const something = await loadSomething()
      //expectations here
    })
  })
})

Run Code Online (Sandbox Code Playgroud)

将 Pact 交互格式转换为 Cypress 路由格式,并去除 Pact 匹配器的函数。

const Matchers = require('@pact-foundation/pact-web').Matchers

// TODO map the request body and query parameters too
const toCypressInteraction = (interaction) => {
  return {
    method: interaction.withRequest.method,
    url: Matchers.extractPayload(interaction.withRequest.path),
    status: interaction.willRespondWith.status,
    headers: Matchers.extractPayload(interaction.willRespondWith.headers),
    response: Matchers.extractPayload(interaction.willRespondWith.body)
  }
}
Run Code Online (Sandbox Code Playgroud)

在赛普拉斯测试中

cy.route(toCypressInteraction(SOME_INTERACTION))
Run Code Online (Sandbox Code Playgroud)

这种方法有以下好处:

  1. 协议很快就生成了。
  2. 该契约是可靠生成的。
  3. Cypress 测试更可靠且更易于调试。
  4. Cypress 测试中使用的请求经过验证是正确的。
  5. 出现“交互膨胀”的可能性较小,即添加交互只是为了测试 UI 功能,而不是因为它们提供了有价值的覆盖范围。

我希望这些信息对您有帮助。我们现在推荐这种方法,而不是在赛普拉斯测试中直接使用模拟服务。