【发布时间】:2019-06-27 19:26:48
【问题描述】:
请注意,这并非特定于 Protractor。问题在于 Angular 2 的内置 Testability service,Protractor 恰好使用了它。 Protractor 通过调用waitForAngular 调用Testability.whenStable。我遇到了一些失败的代码。
我的测试代码如下所示:
await renderFooView();
await interactWithFooView();
第二行失败:
Failed: No element found using locator: By(css selector, foo-view)
当 Protractor 继续尝试与之交互的代码时,Angular 并未完成渲染“foo-view”。
如果我在两者之间添加一个睡眠,它会起作用:
await renderFooView();
await browser.sleep(1000);
await interactWithFooView();
显然我不想那样做。 Protractor 的大部分价值对我来说是“等待角度”机制,它消除了我的脚本中的“等待 X”噪音。我想要的是这样的:
await renderFooView();
await browser.waitForAngular();
await interactWithFooView();
事实上,我不应该手动执行那条中间线。每当我拨打与浏览器交互的电话时,Protractor 都会自动执行此操作。
经过一番挖掘,我发现 Protractor 正在进行调用,它工作正常,但 Angular 2 中的底层可测试性机制似乎已损坏。
在 Angular 2 下,Protractor 的“waitForAngular”执行如下操作:
let rootElement = window.getAllAngularRootElements()[0];
let testability = window.getAngularTestability(rootElement);
testability.whenStable(callbackThatResumesScriptExecution);
换句话说,它调用 Angular 的 testability.whenStable 并且仅在 Angular 报告它稳定时恢复执行。如果我在回调中添加一些日志记录:
testability.whenStable(() => {
console.log("isStable:", testability.isStable());
callback();
});
isStable() 在whenStable 回调中始终为真,因此 Angular 肯定会在看起来正确的时间调用。
但是,如果在此回调返回后立即再次轮询 isStable(),则结果为 false。
let pollAngularIsStable = `{
let rootElement = window.getAllAngularRootElements()[0];
let testability = window.getAngularTestability(rootElement);
return testability.isStable();
}`;
await renderFooView();
await browser.waitForAngular();
console.log(await browser.executeScript(pollAngularIsStable)); // => false
await interactWithFooView();
我已经编写了自己的browser.waitForAngular 版本,我看到的结果完全相同:
let waitForAngularStable = `
let callback = arguments[arguments.length - 1];
let rootElement = window.getAllAngularRootElements()[0];
let testability = window.getAngularTestability(rootElement);
testability.whenStable(callback);
`;
await renderFooView();
await browser.executeAsyncScript(waitForAngularStable);
console.log(await browser.executeScript(pollAngularIsStable)); // => false
await interactWithFooView();
如果我编写一个手动轮询 isStable() 直到它返回 true 的例程,这会修复我的脚本:
async function waitForAngular() {
let ms;
for (ms=0; ms<10000; ++ms) {
await browser.sleep(1);
if (await browser.executeScript(pollAngularIsStable)) {
break;
}
}
console.log(`Waited ${ms}ms for Angular to be stable.`);
}
await renderFooView();
await waitForAngular(); // usually waits < 50ms
console.log(await browser.executeScript(pollAngularIsStable)); // => true
await interactWithFooView(); // always works now
那么...为什么轮询isStable() 有效,但等待whenStable() 回调无效?更重要的是,为什么whenStable() 报告应用程序稳定,而下一个调用(无中间代码)显示isStable() 为假?
【问题讨论】:
-
不幸的是,在某些地方仍然需要
await browser.sleep(...)以使规范更加稳定。我希望我们也可以完全依赖内置机制。 -
如果你使用我上面展示的 waitForAngular 的最终版本,你的代码中不需要任意长的
browser.sleep。它将像量角器一样工作。
标签: angular protractor angular-test