Angular Protractor使用中报错ScriptTimeoutError的问题
最近在使用 Protractor 和 Cucumber 来给 Angular 项目写自动化,写的过程中遇到了一个困扰了我很长时间的问题,经过不断搜索,试验,终于找到解决办法。
测试用例很简单,就是打开一个登录页面,填写用户名密码,登录。Cucumber 的 feature 和 step definition 写好后,运行 ng e2e
会打开这个页面,然后就不动了,过一段时间 console 会报一个 ScriptTimeoutError 的错误。这个是由于在我的 Angular 项目里有一个 interval 的进程,是用于不断向服务器检查当前代码是否是最新代码,如果不是,则刷新页面以获取最新代码。由于这个是一个持续性的进程,没有终止时间,而 Protractor 默认是执行每一个动作之后会等待 Angular 页面响应完毕,然后才会运行下边的代码。这就导致 Protractor 一直在等这个进程执行完,而这个进程是不会自动结束的,所以就 timeout 了。解决办法如下:
# 方法一:让该进程在 Angular 外部运行
Angular 有一个 NgZone 的概念,Angular 项目运行的时候都在一个 Zone 里,只要把这个 interval 放在外边就不会影响 Protractor 对于 Angular 页面是否完成的判断了,具体代码如下:
this.ngZone.runOutsideAngular(() => {
setInterval(() => {
this.ngZone.run(() => {
// 该进程代码
});
}, 3000);
});
2
3
4
5
6
7
如果是用的 Observable.interval()
也一样
this.ngZone.runOutsideAngular(() => {
Observable.interval(3000).subscribe(() => {
// 该进程代码
});
});
2
3
4
5
提示
使用 ngZone.run()
可以让里边的代码重新获得与 Angular 页面交互的能力
这样这个进程就不会影响 Protractor 了
# 方法二:把 Protractor 对于 Angular 页面的检测关掉
在 Protractor 的 config 文件 protractor.conf.js
中的 onPrepare(){}
里写上 browser.waitForAngularEnabled(false);
就可以把这个功能给关掉,这样 Protractor 就不会自动等待 Angular 的页面加载完成了。
但是这样做的弊端是这样就需要自己去写 wait
语句来等待页面加载了。比如:点击按钮后要等待 API 响应,等待某一个页面元素产生某种变化之后再进行下一步操作。本来 Protractor 的这个功能是可以帮你省去写 wait
语句的麻烦的。所以如果可以使用方法一就尽量使用方法一,实在不行再考虑使用方法二。
提示
browser.waitForAngularEnabled(false);
也可以直接用在测试代码中,用于测试中间状态。比如:点击一个按钮,browser.waitForAngularEnabled(false);
检查该按钮在响应结束之前是否可以再次点击。记得检查完之后要 enable 回来 browser.waitForAngularEnabled(true);
这样之后的代码就会回归正常的状态了