我正在测试一个应用程序,该应用程序有一个按钮,该按钮会导致屏幕上另一个元素中的值发生更改,而无需重新加载页面。然而,该值的变化几乎是即时的,或者需要几秒钟。显然,我不想使用一些任意的等待,并且我不想使用(硬)断言 - 这可能会导致测试因失败而停止,而不是失败......所以我'一直在研究 cypress-wait-until (https://www.npmjs.com/package/cypress-wait-until)。我已经写了各种变体,大致如下:
waitForElemToChange(elem) {
elem.invoke('attr', val').then((initialVal) => {
cy.waitUntil(() => elem.invoke('attr', 'value').then((val) => val != initialVal))
}
但到目前为止没有任何效果 - 大多数尝试都导致“重试超时:cy.invoke() 出错,因为您的主题是:null。您无法在 null 值上调用任何函数,例如 attr。”
但它是同一个主题,只是在上一行中返回了一个值??
这看起来太复杂了。我会通过等待
initialValue
,然后等待不同的值来实现它。
cy.get('input').should('have.value', 'myInitialValue'); // wait for initial value
cy.get('input').should('not.have.value', 'myInitialValue'); // wait for it to change
.should()
将重试断言最多 4 秒。
如果“几秒”指的是超过 4 秒,则可以添加超时选项,这比任意等待更好,因为一旦断言通过,它就会继续。
我假设您的元素是输入,因为您正在访问
val
而不是 text
。
我浏览了你的函数,我认为使用上存在几个问题。这可能是与 cypress 相关的问题或等待插件。
首先,在您的代码中,我认为使用 elem 您尝试传递类似
cy.get("path")
的内容。问题是,当您第一次调用 elem 并尝试重新使用 elem 时,它会变为 null。因此,如果您期望 cy.get('path')
运行两次,那么它不会发生。
其次,由于某种原因,在你的等待中,
cy.waitUntil(() => elem.invoke('attr', 'value').then((val) => val != initialVal))
//This part becomes true all the time.
elem.invoke('attr', 'value').then((val) => val != initialVal))
第三,
invoke('attr', 'value')
不会捕获对象的变化属性。请注意,这些观察结果是通过尝试您尝试做的事情收集的。我需要做额外的研究来找出函数的确切行为。
考虑到上述事实,我对代码做了一些更改。
示例 1:在 waitUntil 中获取新值并检查变量更改
const CheckElementChange = (path) => {
//get the initial value of your object
cy.get(path).invoke('attr', 'value').then($initialVal => {
//It's better if you can do your click operation here
//Wait untill the element changes
cy.waitUntil(() =>
cy.get(path).then($newVal =>
$newVal[0].value !== $initialVal),
//optional timeouts and error messages
{
errorMsg: "was expecting some other Value but got : " + $initialVal,
timeout: 10000,
interval: 500
}
).then(() => {
cy.log("Found a difference in values")
})
})
}
示例 2:在新值提取之后移动 waitUntil 并等待 newVal !== initialVal 为 true
const CheckElementChangew = (path) => {
//get the initial value of your object
cy.get(path).invoke('attr', 'value').then($initialVal => {
//It's better if you can do your click operation here
//Wait untill the element changes
cy.get(path).then($newVal => {
cy.waitUntil(() => $newVal[0].value !== $initialVal, {
//optional timeouts and error messages
errorMsg: "was expeting some other Value but got : " + $initialVal,
timeout: 10000,
interval: 500
}).then(() => {
cy.log("Found a difference in values")
})
})
})
}
用途:
it('test',() => {
CheckElementChange2("#fname");
})
注意:如果您期望点击时值会发生变化,除非值变化有几秒的时间间隔,否则最好在提取初始值后再点击(如上面代码中的注释)。
您不需要为此使用插件,您只需要:
cy.wrap({}).then(() => Promise((resolve) => { ... });
在承诺之内,想做什么就做什么;属性更改、文档加载等等。
执行此操作的各种插件所做的就是将其包装在如下实用函数中:
const waitForProperty = (checkForProperty) => cy.wrap({}).then(() => new Cypress.Promise((resolve) => {
// run this every 500ms until our condition is met or 60 seconds has passed.
let elapsed = 0;
const checkIfResolvedYet = () => {
elapsed += 500;
if (checkForProperty()) {
resolve();
return;
}
// If it didn't resolve, check again in 500ms
if (elapsed < 60000) {
setTimeout(checkIfResolvedYet, 500);
}
};
// Run initial check
checkIfResolvedYet();
}));
所以你可以这样使用它:
it("wait for property", () => {
cy.visit("/");
cy.get("#app").then(($app) => {
// Some deferred thing here triggers a property change to wait for
setTimeout(() => {
$app.attr("data-test", "test-123");
}, 1000);
// Wait for the property to be set
waitForProperty(() => {
const value = $app.attr("data-test");
cy.log("during query: " + value);
if (value === "test-123") {
return true;
}
});
cy.log("the property is now set");
});
});
查询期间记录:未定义
查询期间记录:未定义
查询期间记录:test-123
记录属性现已设置
是的,这些插件可以节省您编写大约 10 行代码。
...但是如果 you 编写代码,则可以添加
cy.log
语句来准确调试条件未触发的原因。
需要使用插件就使用插件;但使用
waitUntil
可能会导致神秘的“永远无法工作”的情况,无法调试任何重要的情况。
一般来说,如果一个插件可以被复制 < 10 lines of code, you may get more value from copying the equivalent code into your tests folder than from using the plugin.