关于Protractor在Angular同步和非Angular应用页面的测试
这是关于Angular同步相关内容的一篇名为“不可怕的角度计”的文章。
与Angular的同步
Protractor是作为Angular项目的一部分开发的,因此具有针对Angular专门优化的功能。其中之一就是与Angular同步。
在这里所说的”同步”是指具体等待”$digest loop”、”$timeout”和”$http”等待AJAX请求完成。这样一来,我们就不需要像其他的E2E测试框架那样进行休眠或者轮询等待获取到期望值。我们可以编写更快速、减少不确定性的测试。
需要直接执行,则需要调用browser.waitForAngular()。简单来说,这是通过在浏览器中调用Angular内置的$$testability.whenStable(callback)方法来实现的。
我认为通常情况下没有必要直接执行browser.waitForAngular()。如果使用element()或element.all()等方法,当执行click()等命令时,它会自动调用它,并选择元素执行命令。
在非 Angular 应用的页面上进行测试。
使用Angular非常方便,在Angular应用程序以外的页面上使用Protractor,需要将此行为设置为关闭。
browser.ignoreSynchronization = true;
通过这一方法,将能够在非Angular应用程序的页面上进行测试。
在非Angular应用的页面上等待。
在非 Angular 应用程序页面中需要等待 AJAX 请求等的情况下,您需要自己编写代码来进行等待。WebDriverJS 提供了两种方法。
一种方法是使用sleep()函数,它可以等待一段特定的时间。虽然简单,但在某些情况下可能会导致处理时间过长而失败,或者为了避免这种情况而过长地等待,从而延长测试执行时间,所以不太推荐使用。
browser.sleep(1000);
还有一种方法,就是等待满足某个条件。这个条件可以用函数来表示,函数中会返回一个解决为布尔值的Promise。这确实有点麻烦。
var message = element(by.css('.message'));
browser.wait(function() {
return message.getText().then(function(text) {
return text === 'Hello, World!'
});
}, 3000);
您还可以传递 WebDriverJS 提供的条件对象作为函数的替代。请查看 WebDriverJs 的 until 命名空间以获取更多详细信息。
var message = element(by.css('.message'));
browser.wait(protractor.until.elementTextIs(message, 'Hello, World!'), 3000);
嗯,對於使用Protractor測試這樣的頁面是否必要存在這個問題……。
Angular应用程序与非Angular页面的混合
通过切换 browser.ignoreSynchronization 标志,您可以在 Angular 应用程序和非 Angular 页面之间进行测试。这个标志的评估不是在实际操作浏览器之前,而是在调用 click() 等方法之后,所以基本上在命令和命令之间切换标志也会按预期工作。
例如,假设我们要编写一个测试来检查系统是否能够在登录页面不是使用Angular应用程序而是在登录后转到Angular应用程序的情况下正常工作。基于上述原因,以下代码将能够正常运行。
var loginIdField = element(by.name('login_id'));
var passwordField = element(by.name('password'));
var loginButton = element(by.buttonText('ログイン'));
var usernameDisplay = element(by.binding('username'));
browser.ignoreSynchronization = true;
// Angular と同期しない。
browser.get('index.html');
loginIdField.sendKeys('protractaro');
passwordField.sendKeys('P@ssw0rd');
loginButton.click();
browser.ignoreSynchronization = false;
// ここから Angular と同期する。
expect(usernameDisplay.getText()).toEqual('プロトラク太郎');
如果在then()中有命令发出的情况下
在调用then()方法时,需要注意发出命令的顺序。以下是一个不自然的例子,它会导致失败。这是因为在browser.ignoreSynchronization = false;之后,passwordField.sendKeys(‘P@ssw0rd’);行被执行,而在非Angular应用页面等待Angular。
browser.ignoreSynchronization = true;
browser.get('index.html');
// Angular と同期しない。
loginIdField.sendKeys('protractaro')
.then(function() {
// Angular と同期しようとする!
passwordField.sendKeys('P@ssw0rd');
});
// Angular と同期しない。
loginButton.click();
browser.ignoreSynchronization = false;
// Angular と同期する。
expect(usernameDisplay.getText()).toEqual('プロトラク太郎');
我认为这种情况并不常见,但如果有必要的话,我认为最好将测试用例(it())分开。这是因为需要等待所有命令执行完毕后才能进入下一个测试用例。
it('should login', function() {
browser.ignoreSynchronization = true;
browser.get('index.html');
// Angular と同期しない。
loginIdField.sendKeys('protractaro')
.then(function() {
// Angular と同期しない。
passwordField.sendKeys('P@ssw0rd');
});
// Angular と同期しない。
loginButton.click();
});
it('should show username', function() {
browser.ignoreSynchronization = false;
// Angular と同期する。
expect(usernameDisplay.getText()).toEqual('プロトラク太郎');
});
整理一下
-
- Protractor は Angular アプリには向いている(当たり前)。
-
- Angular じゃないページで非同期な何かを待つ必要がある場合、つらいけどできなくはない。
-
- Angular アプリとそうでないページを混在させたシステムもテスト可能。
- そういう時は、Angular アプリとそうでないページでテストケースを分けると安心。