我自己也在看同样的事情,在某种程度上我希望你能在这个问题上给我一个答案。:-)
话虽如此,量角器似乎足够新,以至于没有人真正知道答案,我想这使我的答案与下一个人一样好。
首先,我使用量角器入门页面上描述的页面对象表示法,朝向底部:https ://github.com/angular/protractor/blob/master/docs/getting-started.md
这提供了一个模块化的元素,我在这里的观点是,我最终得到了一组类,每页一个,抽象掉了一些细节。因此,例如,我可能有一个“foo”类,其中包含“foo.get”和“foo.validate(id, name, otherData)”等抽象。这将是一种提取重复代码的方法。
我还没有解决的一点是如何创建一个模块库,然后将它们组装成一组场景。不过我有几个想法:
- 潜在的问题是相互包含javascript文件的能力——这实际上并不作为一种能力存在。有一些我不想使用的第三方库,而且我还没有看到使用 Angular 的模块功能来做到这一点的方法。
- End 2 end 测试可能非常依赖于测试的顺序。因此,一个测试可能会创建数据,然后另一个测试可能会使用该数据。例如,如果您想要一个让人们登录的测试,您可能需要一个首先注册人们的测试。您可能不想将注册放在您运行的每个测试的前面。因此,无论如何,您可能需要对测试场景的顺序进行大量控制
- 因此,一种选择是将所有内容放在一个非常大的文件中。这与我们在学校学到的一切都背道而驰,但我还没有真正想出一个行不通的理由。然后,您可以编写函数和抽象来满足您的需求。
- 如果你按照它进入下一阶段,另一种选择是编写一系列具有严格命名约定的 javascript 文件,然后在执行它们之前使用 grunt 为你连接它们。因此,例如:
- 一组名为 xxxx.page.scenario.js 的文件,其中包含“页面对象”定义 - 基本上是每个页面的辅助方法
- 一组名为 xxxx.functions.scenario.js 的文件,其中包含您的场景的常见组件 - 所以也许您有一个注册和登录操作集,并且您将其制作成一个库函数
- 一组名为 nnxx.scenarios.scenario.js 的文件,其中包含实际的脚本本身。它们在开头(nn)编号,因此我们可以将它们以可靠的顺序连接起来,从而控制我们的脚本运行的顺序
我还没有说这是一个好主意,只是至少从表面上看它可以工作,并且会产生预期的结果。我主要担心的是它感觉很脆弱——所以随着测试套件规模的扩大,它可能会变得非常难以维护。也许另一种方法是,而不是对场景进行编号,而是将它们定义为依赖项,并确保任何给定的脚本在它声明自己依赖的任何脚本之后运行。这可能也允许对脚本进行子集化 - 所以你可以说“运行 bar 脚本”,框架会知道 bar 脚本需要首先运行 foo 脚本,也许是登录脚本。但是可以忽略所有其他脚本。
编辑:我认为 astrolabe 在这里可能是一个很好的答案,看起来它明确允许您将测试模块化。 https://github.com/stuplum/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)); } }
}
);
clubs.scenario.js:
/**
* 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 更新该教程:http ://technpol.wordpress.com/2013/11/16/5-end-to-end-testing/