如何使赛普拉斯等待具有多个结果的异步搜索完成而不会导致测试失败

Posted

技术标签:

【中文标题】如何使赛普拉斯等待具有多个结果的异步搜索完成而不会导致测试失败【英文标题】:how to make cypress wait for a async search with multiple result complete without causing the test to fail 【发布时间】:2021-11-28 14:53:15 【问题描述】:

我有一个典型的搜索,其中用户在输入中键入一些文本,异步工作完成并且包含结果的表格被正确更新。

我的测试必须等待此搜索步骤,然后断言有关结果的业务规则,例如表记录是否符合编辑条件。

每次我运行一个完整的测试电池(大约 80 个测试文件)时,涉及该搜索的一两个测试不可避免地会失败。但是,如果在那之后立即单独运行相同的测试,则测试通过。这很痛苦,并且使 CI/CD 中的 e2e 测试对项目毫无意义。

我已阅读赛普拉斯文档中有关易碎测试的内容,并在 *** 和 GitHub 中搜索过问题,但结果完全失败。这是一部戏剧。

这是其中一项测试:

import  searchList  from '../helpers';
import  createFluxoIniciado, randomFluxoNome  from './common';
import  fluxoSelectors  from './selectors';

describe('fluxos finish', () => 
  it('can manually finish a fluxo INICIADO', () => 
   // feed data to be searched
    const fluxoNome = randomFluxoNome();
    createFluxoIniciado( fluxoNome );

    // search
    searchList(fluxoNome);

    // do something with search results
    fluxoSelectors.fluxos.view().click();
    fluxoSelectors.finish().click();
    fluxoSelectors.confirm().click();

   // serach again
    searchList(fluxoNome);
    cy.contains('FINALIZADO');
  );
);

searchList 中的代码有时会出现问题。它使用推荐的回调策略here。如果不是所有行都有搜索到的文本,代码会尝试导致重试。

export function searchList (text) 
  cy.get('#searchText')
    .scrollIntoView()
    .type(text)
    .blur();

  cy.get('tbody tr').should($trs => 
    $trs.each((i, $tr) => 
      expect($tr).to.contain(text);
    );
  ,  timeout: 15000 );

以下是运行所有测试执行中的测试失败示例:

【问题讨论】:

【参考方案1】:

问题显然是由.blur() 和测试行之间的异步获取引起的。

您正确地尝试通过 .should(callback) 使用 Cypress 重试,但如果回调很复杂或有多个步骤,它可能不会重试正在更改的元素(表格行)。

理想情况下,您希望cy.get(...).should(...) 尽可能简单,并首先测试表加载是否已完成。

// wait for expected number of rows
cy.get('tbody tr', timeout: 15000).should('have.length', 5)  

cy.get('tbody tr').each($tr => 
  expect($tr).to.contain(text);
)

但是你有一个随机化器,所以可能无法明确地测试行数。

另一种方法,为text 测试整个表(.contains() 也检查子文本)

// wait for text to appear somewhere
cy.get('tbody tr', timeout: 15000).should('contain', text)  

cy.get('tbody tr').each($tr => 
  expect($tr).to.contain(text);
)

您还可以在 api 调用的开始和结束之间添加拦截

export function searchList (text) 

  cy.intercept('search/api/endpoint').as('search')

  cy.get('#searchText')
    .scrollIntoView()
    .type(text)
    .blur();

  cy.wait('@search')   // wait for api response

  cy.get('tbody tr', timeout: 15000).should('contain', text) 

  cy.get('tbody tr').each($tr => 
    expect($tr).to.contain(text);
  )


我刚刚注意到您在 .should() 上有 timeout 选项,但那是错误的地方, 见Timeouts

cy.get('input',  timeout: 10000 ).should('have.value', '10')
// timeout here will be passed down to the '.should()'
// and it will retry for up to 10 secs

这可能会成功

cy.get('tbody tr',  timeout: 15000 )
  .should($trs => 
    $trs.each((i, $tr) => 
      expect($tr).to.contain(text);
    );
  )

【讨论】:

关于第一个建议,你是对的,测试期间使用的数据是随机的,这是一个限制。正如你所说,搜索后的行数是不可预测的。 第三个也不能用,我想。因为异步工作比 api 服务器调用有更多的部分。有时,例如,由于客户端的缓存策略,网络请求甚至没有完成。搜索“完成”的唯一可靠信号是表格中向用户显示的正确结果列表。 将检查第二个建议并返回。感谢您的快速答复。 您可能还想在这里合并这些想法How to run the same test again and again to confirm it is flake-free 你好。我尝试了第二种方法,它也没有奏效。 “应该包含”断言很好,但之后的立即命令失败。我后来回忆起来,这是我尝试的第一种方法。搜索后,测试(有时)在尝试获取表格行中的按钮以编辑记录时失败。 cypress 抱怨该元素已分离或不可用。如果我没记错的话,这是使用像 vue 这样的响应式框架的副作用。我想我会在搜索后任意等待:(——但再次感谢。

以上是关于如何使赛普拉斯等待具有多个结果的异步搜索完成而不会导致测试失败的主要内容,如果未能解决你的问题,请参考以下文章

在赛普拉斯测试中加载页面后,如何可靠地等待 XHR 请求?

赛普拉斯报告者结果文件名

赛普拉斯:只运行一项测试

如何将地图对象设置为赛普拉斯结果 json 文件

赛普拉斯测试因断言等于 DOM 标题而失败

有没有办法强制赛普拉斯在同一个标​​签而不是另一个标签中打开