Protractor 我可以将我的测试划分为单独的规范,然后从另一个规范调用它们吗?还是使用辅助函数更好?
刚刚开始E2E测试的量角器,我在测试用例结构方面遇到了一些问题 不确定我是否可以将测试划分为单独的规范,然后从另一个规范调用它们,或者我如何制作好的助手函数来处理这个问题 我通过中继器查找元素,然后我想对中继器中每个元素的每个操作进行测试。有点像这样:Protractor 我可以将我的测试划分为单独的规范,然后从另一个规范调用它们吗?还是使用辅助函数更好?,protractor,Protractor,刚刚开始E2E测试的量角器,我在测试用例结构方面遇到了一些问题 不确定我是否可以将测试划分为单独的规范,然后从另一个规范调用它们,或者我如何制作好的助手函数来处理这个问题 我通过中继器查找元素,然后我想对中继器中每个元素的每个操作进行测试。有点像这样: describe('tasty', function () { 'use strict'; var ptor; beforeEach(function () { ptor = protractor.get
describe('tasty', function () {
'use strict';
var ptor;
beforeEach(function () {
ptor = protractor.getInstance();
ptor.get('http://localhost:8000/');
});
it('Should sample three tasty fruits of every kind on my shopping list.', function () {
ptor.findElement(protractor.By.className('fruitstore')).click();
var fruitshelves = ptor.findElements(protractor.By.repeater('fruit in fruits').column('header'));
fruitshelves.then(function(arr) {
for (var i=0;i<arr.length; i++) {
// Pick up three fruits of this kind from the shelf and put in shopping cart
// Should be listed on my shopping list
// Open the wallet
// Should have money
// Pay for the fruits and put it in your shopping bag
// Should be able to complete the transaction
// For each one of the fruits in your shopping bag
// Take a bite
// Should be tasty
}
});
});
});
description('tasty',函数(){
"严格使用",;
变阻器;
beforeach(函数(){
ptor=量角器.getInstance();
获得http://localhost:8000/');
});
它('应该在我的购物清单上每种水果中品尝三种美味的水果'),功能(){
ptor.findElement(progrator.By.className('fruitstore')).click();
var fruitsheels=ptor.findElements(量角器By.repeater(‘水果中的水果’)。列(‘标题’);
水果架。然后(功能(arr){
对于(var i=0;i我自己也在看同样的事情,在某种程度上我希望你能为我回答这个问题。:-)
话虽如此,看来量角器是新的足够,没有人真正知道答案,我猜这使我的答案和其他人一样好
首先,我使用了“量角器入门”页面中描述的页面对象表示法,如下所示:
这提供了模块化的元素,我的观点是,我最终得到了一组类,每页一个,这些类抽象掉了一些细节。因此,例如,我可能有一个“foo”类,其中包含诸如“foo.get”和“foo.validate(id,name,otherData)”之类的抽象。这将是提取重复代码的一种方法
我还没有弄清楚的一点是如何创建一个模块库,然后将它们组装到一组场景中。不过我有一些想法:
潜在的问题是在彼此中包含javascript文件的能力——这实际上并不是一种功能。有一些第三方库,我不想使用,我还没有看到使用Angular的模块功能来实现这一点的方法
End 2 End测试可能非常依赖于测试的顺序。因此,一个测试可能会创建数据,另一个测试可能会使用该数据。例如,如果您想要一个让人登录的测试,您可能需要一个先注册人的测试。您可能不想在运行的每个测试的前面都注册。因此,您可能需要无论如何,bly都需要对测试场景的顺序进行大量控制
因此,一种选择是把所有的东西都放在一个很大的文件中。这与我们在学校学到的所有东西都不符,但我还没有找到一个不起作用的理由。然后你就可以把函数和抽象写在心里了
如果您按照这个步骤进行下一步,另一个选项是编写一系列具有严格命名约定的javascript文件,然后在执行它们之前使用grunt将它们连接起来。例如:
- 一组名为xxxx.page.scenario.js的文件,其中包含“页面对象”定义,基本上是每个页面的帮助器方法
- 一组名为xxxx.functions.scenario.js的文件,其中包含场景的常见组件,因此您可能有一组注册和登录操作,并将其放入库函数中
- 一组名为nnxx.scenarios.scenario.js的文件,其中包含实际脚本本身。这些文件在开始时编号(nn),因此我们可以以可靠的顺序连接它们,从而控制脚本的运行顺序
我还没有说这是个好主意,只是说它至少表面上看起来是可行的,并且会产生预期的结果。我主要担心的是它感觉很脆弱——因此随着测试套件的规模不断扩大,它可能会变得很难维护。也许另一种方法是,不给场景编号,而是将它们定义为依赖项,并确保任何给定脚本在其声明自己依赖的任何脚本之后运行。这可能还允许对脚本进行子集设置,因此您可以说“运行bar脚本”框架会知道bar脚本首先需要运行foo脚本,也许还需要登录脚本,但是可以忽略所有其他脚本
编辑:我认为astrolabe可能是一个很好的答案,它似乎明确地允许你模块化你的测试..我刚刚用它完成了一个概念验证,它似乎做了我所希望的一切。它的代码最终类似于:
clubs.part.scenario.js:
/**
* Partial for the page objects associated with clubs
*/
var Page = require('astrolabe').Page;
module.exports = Page.create({
url: { value: 'UI/index.html#clubs' },
title: { get: function() { return this.findElement(this.by.id('title')); } },
description: { get: function() { return this.findElement(this.by.id('description')); } },
clubTableElement: { value: function(rowNum, columnBinding) {
return this.findElement(this.by.repeater('club in clubs').row(rowNum).column(columnBinding)); } }
}
);
/**
* End to end tests for the club functionality
*/
var homePage = require('../home/home.part.scenario.js');
var clubsPage = require('./clubs.part.scenario.js');
describe( 'Navigate to club list page', function() {
it ( 'should allow navigation to the club list page', function() {
homePage.go();
expect(homePage.clubsLink.getText()).toEqual('Clubs');
homePage.clubsLink.click();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
it ( 'should allow us to go directly to the club list page', function() {
clubsPage.go();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
});
clubs.scenario.js:
/**
* Partial for the page objects associated with clubs
*/
var Page = require('astrolabe').Page;
module.exports = Page.create({
url: { value: 'UI/index.html#clubs' },
title: { get: function() { return this.findElement(this.by.id('title')); } },
description: { get: function() { return this.findElement(this.by.id('description')); } },
clubTableElement: { value: function(rowNum, columnBinding) {
return this.findElement(this.by.repeater('club in clubs').row(rowNum).column(columnBinding)); } }
}
);
/**
* End to end tests for the club functionality
*/
var homePage = require('../home/home.part.scenario.js');
var clubsPage = require('./clubs.part.scenario.js');
describe( 'Navigate to club list page', function() {
it ( 'should allow navigation to the club list page', function() {
homePage.go();
expect(homePage.clubsLink.getText()).toEqual('Clubs');
homePage.clubsLink.click();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
it ( 'should allow us to go directly to the club list page', function() {
clubsPage.go();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
});
我对这个结构很满意,它不是万能的,但它能做大多数事情。我提供的示例代码来自我与angularjs一起工作了一段时间的教程,我正在为e2e测试和Rails 4更新它,如果你想了解与之相关的上下文:我来问这个问题是为了寻找一个可以在量角器中的spec文件之间共享helper函数。如果其他人也在寻找相同的函数,那么由于量角器只是在节点中运行,所以您所需要做的就是var helpers=require('./您的helper文件')
基于@langliman答案,我已经成功地实现了所需的行为
注意login.spec.js
和login.page.js
应位于同一文件夹中
Login.page.js文件:
var LoginPage = function (ptor) {
//following PageObject pattern define the functions here.
}
module.exports.getLoginPage = function (ptor) {
return new LoginPage(ptor);
};
login.spec.js文件:
(function () {
'use strict';
describe('login page', function () {
var ptor = protractor.getInstance();
var loginPageBuilder = require('./Login.page.js');
var loginPage = loginPageBuilder.getLoginPage(ptor);
it('should login as admin', function () {
loginPage.visit();
loginPage.enterUsername('user');
loginPage.enterPassword('password');
loginPage.login();
});
});
}());
如果您想要共享设置和before/after函数以及helper方法,一种解决方案是要求spec helper进行测试,而不是要求spec helper进行测试
conf.js
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['e2e/spec.js']
}
var chai = require('chai'),
homepage = require('./homepage.js'),
signin = require('./signin.js');
chai.should()
browser.baseUrl = 'http://localhost:3000'
homepage.test()
signin.test()
exports.test = function() {
describe('homepage', function() {
it('should have the right title', function() {
browser.get('/')
browser.getTitle().then(function(title){
title.should.eq('Home')
})
});
});
}
exports.test = function() {
describe('signin', function() {
it('should have the right title', function() {
browser.get('/signin')
browser.getTitle().then(function(title){
title.should.eq('Sign in')
})
});
});
}
e2