如何让Cypress执行检查等待循环直至DOM元素发生变化?
如何在Cypress中替代固定等待,实现DOM变化的智能检测?
我之前在Stack Overflow提问过如何检测页面操作引发的DOM变化,收到了基于as()方法捕获状态的优质解答,以下是基于该解答的可运行测试代码:
visitCountryPage(); // 捕获trip ideas的初始状态(静态变量) getTripIdeas().as('initialValues', { type: 'static' }); // 打开筛选菜单 cy .get('#sharedTrips .filtersContainer button') .first() .click(); // 选择筛选选项,触发内容变化 cy .get('#sharedTrips .filtersContainer label[for="experiencesrelax"]') .click() // @todo 能不能不用这种固定等待? .wait(3000); // 捕获变化后的trip ideas状态 getTripIdeas().as('finalValues', { type: 'static' }); // 断言初始状态与最终状态不同 cy.get('@initialValues').then(initial => { cy.get('@finalValues') .invoke('toArray') // 将JQuery<HTMLElement>转为HTMLElement数组 .should('not.deep.eq', initial.toArray()) });
测试步骤
- 访问目标页面
- 对"trip ideas"结果网格进行快照
- 打开筛选弹出菜单
- 选择菜单中的筛选选项
- 等待固定时长直至页面变化 🙈
- 再次对结果网格进行快照
- 验证两次快照内容不同
获取Trip Ideas元素的方法
下面是我封装的获取目标元素的代码:
/** * 根据CSS选择器获取网格元素(比如卡片集合) */ export function getTripIdeas(): Cypress.Chainable<JQuery<HTMLElement>> { return getGridElements( '#sharedTrips section .container div:last-child .card-layout div.column' ); } /** * 通用的网格元素获取方法 */ function getGridElements(cssSelector: string): Cypress.Chainable<JQuery<HTMLElement>> { return cy .get('body') .find(cssSelector); }
优化需求:替代固定等待的智能检测
当前测试中使用了wait(3000)来等待AJAX请求完成,但更符合Cypress设计理念的做法是:告诉Cypress"我期望这个变化发生,请等待最长X秒直到条件满足"。这种方式可以设置较长超时(比如15秒),一旦条件满足会立即通过,无需等待满时长。
在PHP/Selenium中,我会用这样的循环检查方法:
protected function waitUntil(callable $callback, string $reason, $timeout = 10): void { $now = microtime(true); do { usleep(500 * 1000); // 每隔0.5秒检查一次 $withinTimeout = (microtime(true) - $now) < $timeout; $passedTest = $callback($this->getDriver()); } while ($withinTimeout && !$passedTest); if (!$passedTest) { throw new RuntimeException( sprintf('Failed to wait for %s', $reason) ); } }
调用示例:
// 等待推荐内容加载完成 $this->waitUntil( function(RemoteWebDriver $driver) { try { $ideas = $driver->findElements(WebDriverBy::cssSelector( '#sharedTrips section .container div:last-child .card-layout div.column' )); // 初始加载时通常有8个元素,这里判断数量是否大于4 return count($ideas) > 4; } catch (NoSuchElementException $e) { return false; } }, 'trip ideas to populate' );
注:以上仅展示同步语言中循环检查DOM的方式,并非检测DOM内容变化的示例
这种每隔一段时间检查一次DOM,直到条件满足或超时的方式,更符合我的测试习惯。能不能在Cypress中实现类似功能,去掉固定等待以提升测试的健壮性?
内容的提问来源于stack exchange,提问作者halfer
相关产品推荐
相关产品推荐

