【问题标题】:Cannot make Cypress and Pact work together无法使 Cypress 和 Pact 一起工作
【发布时间】:2021-07-20 13:21:43
【问题描述】:

我已经有几个通过赛普拉斯测试的工作项目。 现在我正在尝试使用 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

我正在使用:

  cypress: 7.5.0
  @pact-foundation/pact: 9.16.0

我已经完成的步骤:

  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');
        });
    })
    

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

【问题讨论】:

    标签: javascript testing cypress pact contract


    【解决方案1】:

    在 Pactflow,我们花了大约 6 个月的时间一起使用赛普拉斯和 Pact,使用 Pact 模拟服务从我们的赛普拉斯测试中生成协议,如 https://pactflow.io/blog/cypress-pact-front-end-testing-with-confidence/ 中所建议的那样

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

    1. 我们的赛普拉斯测试比我们的单元测试慢得多(15 分钟以上),因此当我们从赛普拉斯测试生成协议而不是单元测试时,生成协议然后对其进行验证需要更长的时间。
    2. 我们的赛普拉斯测试可能会因与 Pact 无关的原因(例如布局更改、构建节点上的内存消耗问题、UI 库升级等)而失败,这会导致 Pact 无法生成。
    3. 赛普拉斯拦截()和模拟服务不能很好地结合在一起。任何对赛普拉斯没有显式拦截()的请求都会在模拟服务中结束,并且每次页面使用新端点时,请求都会命中模拟服务,然后整个测试失败,即使该端点该特定测试不需要。
    4. 当赛普拉斯测试失败时,赛普拉斯库提供了非常好的调试。当它由于 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")
        }
      }
    }
    
    

    单元测试

    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
        })
      })
    })
    
    

    将 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)
      }
    }
    

    在赛普拉斯测试中

    cy.route(toCypressInteraction(SOME_INTERACTION))
    

    这种方法有以下好处:

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

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

    【讨论】:

      猜你喜欢
      • 2019-07-23
      • 2020-05-26
      • 2016-12-25
      • 1970-01-01
      • 2021-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多