So today I have been working on setting up end-2-end testing for an Angular JS app with Protractor. For writing cleaner tests, I make use of the Page Object pattern as described on the Protractor website.
所以今天我一直在为使用Protractor的Angular JS应用程序设置终端2端测试。为了编写更清晰的测试,我使用了Protractor网站上描述的Page Object模式。
Test scenario: When a user enters the website, he/she needs to login first. By filling in their credentials and clicking a button, the user is redirected to a validation page. There they need to enter a code that has been sent to them by email. In this case, the validation code is always the same when running end-2-end tests (123456
). When the user has entered his/her code, the validation button needs to be clicked in order to enter the landing page (organisations
). There a list of organisations is presented to the user.
测试场景:当用户进入网站时,他/她需要先登录。通过填写凭据并单击按钮,用户将被重定向到验证页面。他们需要输入通过电子邮件发送给他们的代码。在这种情况下,运行end-2-end tests(123456)时验证代码始终相同。当用户输入他/她的代码时,需要单击验证按钮才能进入登录页面(组织)。在那里向用户呈现组织列表。
The Protractor test:
量角器测试:
organisations.spec.js
'use strict';
describe('when selecting organisation', function () {
var page;
var util = require('../../util');
browser.get('/#/login');
// login before each test case
beforeEach(function () {
page = require('./organisations.po');
util.login('username', 'password');
});
// logout after each one
afterEach(function () {
page.logoutButton.click();
});
it('should have a list of organisations', function () {
expect(browser.getCurrentUrl()).toEqual('http://localhost:9111/#/organisations');
expect(page.organisationList.isPresent()).toBe(true);
});
});
organisations.po.js (the page object)
organisations.po.js(页面对象)
'use strict';
var MainPage = function () {
// organisations
this.organisationList = element(by.css('.select-list'));
this.logoutButton = element(by.css('.logout-button'));
};
module.exports = new MainPage();
util.js (code for login process)
util.js(登录过程的代码)
'use strict';
var Util = function () {
this.login = function login(username, password) {
browser.get('/#/login');
var loginPage = require('./spec/login/login.po');
var validationPage = require('./spec/login/validate.po');
// fill in login form
loginPage.username.sendKeys(username);
loginPage.password.sendKeys(password);
// submit login form and navigate to validation page
loginPage.loginButton.click();
// fill in validation form
validationPage.validationCode.sendKeys('123456');
// submit validation form and navigate to landing page
validationPage.submitValidateBtn.click();
}
};
module.exports = new Util();
What happens when running the test: Protractor is able to fill in the user credentials and click the login button. However, the corresponding redirect to the validation page does not occur, causing the expectations to fail as Protractor is not able to locate the page objects since these are on another page. Here is a part of the error message that Protractor produces. Just in case, here can you see the Protractor configuration file that is being used.
运行测试时会发生什么:量角器能够填写用户凭据并单击登录按钮。但是,没有发生相应的重定向到验证页面,导致期望失败,因为Protractor无法找到页面对象,因为它们位于另一页面上。以下是Protractor生成的错误消息的一部分。以防万一,在这里可以看到正在使用的Protractor配置文件。
I tried putting explicit waits (browser.wait(<condition>)
around the button clicks, but that just seems to have the same effect. Could someone point out what I am doing wrong and why that redirect is not happening? Am I missing something obvious here? Thanks in advance!
我尝试在按钮点击周围进行显式等待(browser.wait(
3 个解决方案
#1
1
If your login-pages are non-Angular, double check whether you need to set browser.ignoreSynchronization
to false in Protractor.
如果您的登录页面是非Angular,请仔细检查是否需要在Protractor中将browser.ignoreSynchronization设置为false。
If they are Angular, I would think you'd need to wait for the click
promise to complete after login? Does the following work?
如果他们是Angular,我认为您需要等待点击承诺在登录后完成?以下工作如何?
loginPage.loginButton.click().then(function () {
validationPage.validationCode.sendKeys('123456');
...
}
In this way, Protractor does the wait
(suggested in other answers) itself, provided the login/validation page are indeed done in Angular.
通过这种方式,只要登录/验证页面确实在Angular中完成,Protractor就会进行等待(在其他答案中建议)。
As a related remark, in your beforeEach
and afterEach
, you may also want to use the optional done
callback, so that the actual test only starts after login has been successful:
作为相关注释,在beforeEach和afterEach中,您可能还希望使用可选的完成回调,以便实际测试仅在登录成功后启动:
beforeEach(function (done) {
...
util.login(user, pw).then(done);
}
This does require, though, that your login
method actually returns the promise of the validation click. (I typically let all my page object methods return the promises).
但是,这确实需要您的登录方法实际返回验证单击的承诺。 (我通常让我的所有页面对象方法都返回promises)。
Also related to your page objects, instead of having a Login page object with just the element finders as fields, why not give that page object the more abstract login method that is now in a util class?
也与页面对象有关,而不是只有元素查找器作为字段的登录页面对象,为什么不给那个页面对象一个现在在util类中的更抽象的登录方法?
Furthermore, I tend to give every page object a 'self check' method, that permits checking that the browser is indeed on the expected page. After clicking login on the Login page, I expect to be on the validation page. Thus, the first thing I do is check (assert) that I'm indeed on the validation page. Only then do I actually press the validate button. This tends to give cleaner error messages in case your application's routing is broken.
此外,我倾向于为每个页面对象提供一个“自检”方法,该方法允许检查浏览器确实在预期的页面上。在登录页面上单击登录后,我希望在验证页面上。因此,我做的第一件事是检查(断言)我确实在验证页面上。只有这样我才能按下验证按钮。如果应用程序的路由被破坏,这往往会提供更清晰的错误消息。
I wrote a bit more about state navigation and page objects on my blog.
我在博客上写了更多关于状态导航和页面对象的内容。
#2
0
I've had some issues with code in a part further along interrupting at a spot earlier than you'd expect. For example, as soon as it loads up a page object that has the right path, but maybe an error with the declaration of the page itself, the whole test crashes.
我在某个部分的代码中遇到了一些问题,这些问题比你预期的更早地中断。例如,只要它加载具有正确路径的页面对象,但可能是页面本身声明的错误,整个测试就会崩溃。
First, I would comment everything out after clicking the login button. Then maybe add a 10s sleep. Run the test and watch it. Does it click the button? Does it get past the login screen?
首先,我会在点击登录按钮后对所有内容进行评论。然后可能加10秒睡眠。运行测试并观察它。它是否点击按钮?它是否通过登录屏幕?
If that is working, uncomment one line at a time and see where it crashes. Try keeping the sleep in there to see if it makes a difference. If the sleep does make a difference, that means whatever wait you were using wasn't one that works in that situation.
如果这样做,请一次取消注释一行并查看它崩溃的位置。尽量保持睡眠,看看它是否有所作为。如果睡眠确实有所作为,那就意味着你所使用的等待不是那种在那种情况下起作用的。
Of course, if you hit a line that crashes you every single time, then you can dig further!
当然,如果你每次碰到一条让你崩溃的线路,那么你可以进一步挖掘!
#3
0
what I have found to help work is to force protractor to wait till a page loads by waiting till the title is a specific title.
我发现帮助工作的是强制量角器等到页面加载,等待标题是一个特定的标题。
for example: the title could be login at your login page but then after you hit login the next page title is validation. So you can force protractor to wait till the validation page loads.
例如:标题可以登录您的登录页面,但是在您登录后,下一页标题就是验证。所以你可以强制量角器等到验证页面加载。
NOTE: I don't know the actual names of your titles so I'm just using place holders.
注意:我不知道你的标题的实际名称,所以我只是使用占位符。
'use strict';
var Util = function () {
this.login = function login(username, password) {
browser.get('/#/login');
var loginPage = require('./spec/login/login.po');
var validationPage = require('./spec/login/validate.po');
// fill in login form
loginPage.username.sendKeys(username);
loginPage.password.sendKeys(password);
// submit login form and navigate to validation page
loginPage.loginButton.click();
// wait till our validation page loads then do our validation
browser.wait(protractor.until.titleIs("Validation"), 5000, "✗ Failed to wait for the validation page to load").then(function(){
// fill in validation form
validationPage.validationCode.sendKeys('123456');
// submit validation form and navigate to landing page
validationPage.submitValidateBtn.click();
});
}
};
#1
1
If your login-pages are non-Angular, double check whether you need to set browser.ignoreSynchronization
to false in Protractor.
如果您的登录页面是非Angular,请仔细检查是否需要在Protractor中将browser.ignoreSynchronization设置为false。
If they are Angular, I would think you'd need to wait for the click
promise to complete after login? Does the following work?
如果他们是Angular,我认为您需要等待点击承诺在登录后完成?以下工作如何?
loginPage.loginButton.click().then(function () {
validationPage.validationCode.sendKeys('123456');
...
}
In this way, Protractor does the wait
(suggested in other answers) itself, provided the login/validation page are indeed done in Angular.
通过这种方式,只要登录/验证页面确实在Angular中完成,Protractor就会进行等待(在其他答案中建议)。
As a related remark, in your beforeEach
and afterEach
, you may also want to use the optional done
callback, so that the actual test only starts after login has been successful:
作为相关注释,在beforeEach和afterEach中,您可能还希望使用可选的完成回调,以便实际测试仅在登录成功后启动:
beforeEach(function (done) {
...
util.login(user, pw).then(done);
}
This does require, though, that your login
method actually returns the promise of the validation click. (I typically let all my page object methods return the promises).
但是,这确实需要您的登录方法实际返回验证单击的承诺。 (我通常让我的所有页面对象方法都返回promises)。
Also related to your page objects, instead of having a Login page object with just the element finders as fields, why not give that page object the more abstract login method that is now in a util class?
也与页面对象有关,而不是只有元素查找器作为字段的登录页面对象,为什么不给那个页面对象一个现在在util类中的更抽象的登录方法?
Furthermore, I tend to give every page object a 'self check' method, that permits checking that the browser is indeed on the expected page. After clicking login on the Login page, I expect to be on the validation page. Thus, the first thing I do is check (assert) that I'm indeed on the validation page. Only then do I actually press the validate button. This tends to give cleaner error messages in case your application's routing is broken.
此外,我倾向于为每个页面对象提供一个“自检”方法,该方法允许检查浏览器确实在预期的页面上。在登录页面上单击登录后,我希望在验证页面上。因此,我做的第一件事是检查(断言)我确实在验证页面上。只有这样我才能按下验证按钮。如果应用程序的路由被破坏,这往往会提供更清晰的错误消息。
I wrote a bit more about state navigation and page objects on my blog.
我在博客上写了更多关于状态导航和页面对象的内容。
#2
0
I've had some issues with code in a part further along interrupting at a spot earlier than you'd expect. For example, as soon as it loads up a page object that has the right path, but maybe an error with the declaration of the page itself, the whole test crashes.
我在某个部分的代码中遇到了一些问题,这些问题比你预期的更早地中断。例如,只要它加载具有正确路径的页面对象,但可能是页面本身声明的错误,整个测试就会崩溃。
First, I would comment everything out after clicking the login button. Then maybe add a 10s sleep. Run the test and watch it. Does it click the button? Does it get past the login screen?
首先,我会在点击登录按钮后对所有内容进行评论。然后可能加10秒睡眠。运行测试并观察它。它是否点击按钮?它是否通过登录屏幕?
If that is working, uncomment one line at a time and see where it crashes. Try keeping the sleep in there to see if it makes a difference. If the sleep does make a difference, that means whatever wait you were using wasn't one that works in that situation.
如果这样做,请一次取消注释一行并查看它崩溃的位置。尽量保持睡眠,看看它是否有所作为。如果睡眠确实有所作为,那就意味着你所使用的等待不是那种在那种情况下起作用的。
Of course, if you hit a line that crashes you every single time, then you can dig further!
当然,如果你每次碰到一条让你崩溃的线路,那么你可以进一步挖掘!
#3
0
what I have found to help work is to force protractor to wait till a page loads by waiting till the title is a specific title.
我发现帮助工作的是强制量角器等到页面加载,等待标题是一个特定的标题。
for example: the title could be login at your login page but then after you hit login the next page title is validation. So you can force protractor to wait till the validation page loads.
例如:标题可以登录您的登录页面,但是在您登录后,下一页标题就是验证。所以你可以强制量角器等到验证页面加载。
NOTE: I don't know the actual names of your titles so I'm just using place holders.
注意:我不知道你的标题的实际名称,所以我只是使用占位符。
'use strict';
var Util = function () {
this.login = function login(username, password) {
browser.get('/#/login');
var loginPage = require('./spec/login/login.po');
var validationPage = require('./spec/login/validate.po');
// fill in login form
loginPage.username.sendKeys(username);
loginPage.password.sendKeys(password);
// submit login form and navigate to validation page
loginPage.loginButton.click();
// wait till our validation page loads then do our validation
browser.wait(protractor.until.titleIs("Validation"), 5000, "✗ Failed to wait for the validation page to load").then(function(){
// fill in validation form
validationPage.validationCode.sendKeys('123456');
// submit validation form and navigate to landing page
validationPage.submitValidateBtn.click();
});
}
};