AngularJS 应用程序的 Protractor 简介






4.71/5 (8投票s)
在 Visual Studio 中使用 Protractor 对 AngularJS 应用程序进行 E2E 测试
引言
Protractor 是一个用于 AngularJS 应用程序的端到端测试框架。Protractor 在真实浏览器中运行并与应用程序进行交互,模拟用户的行为来运行测试。在 Visual Studio 中集成 Protractor 用于 AngualrJS 应用程序需要进行一些配置。
背景
GitHub 仓库: GitHub 链接
请按照以下步骤将其集成到您的项目中。
步骤 1:安装 Node.js
安装任何一个版本,我都测试过。
安装完成后,通过打开 CMD 窗口(在运行对话框中输入 cmd)来确认安装成功。
命令
node –version
步骤 2:安装 Node.js Tools for VS
这将为 VS 添加 node.js 模板,为 node 项目添加智能感知,并启用 npm 集成。
步骤 3:安装 Web Essentials
这将为您的项目添加很酷的功能,例如 JSHint、最小化、源映射、查找引用、智能感知、捆绑、ZenCoding、验证等。
步骤 4:安装 Java Development Kit
选择任何版本,我测试过 8u66 和 8u65(应大于 8)。
安装完成后,通过打开 CMD 窗口(在运行对话框中输入 cmd)来确认安装成功。
命令
java -version
步骤 5:安装 Protractor(全局或本地)
- 右键单击项目并选择“在此处打开命令提示符”(我们也可以在 CMD 中执行此操作,但目前先留在 VS 环境)。
- 如果您想全局安装 protractor,请执行以下命令:
npm install -g protractor
- 如果您想本地安装 protractor,请执行以下命令:
npm install protractor --save-dev
安装完成后,通过打开 CMD 窗口(在运行对话框中输入 cmd)来确认安装成功。命令
protractor –version
- 如果您想全局安装 protractor,请执行以下命令:
步骤 6:更新 Selenium Standalone Server
- 右键单击项目并选择“在此处打开命令提示符”(我们也可以在 CMD 中执行此操作,但目前先留在 VS 环境)。
- 更新 Selenium 驱动程序,请执行以下命令:
webdriver-manager update
步骤 7:启动 Selenium Standalone Server
- 右键单击项目并选择“在此处打开命令提示符”(我们也可以在 CMD 中执行此操作,但目前先留在 VS 环境)。
- 启动 Selenium 驱动程序,请执行以下命令:
webdriver-manager start
请注意
在您使用 protractor 完成测试之前,请勿关闭此 CMD 窗口。
驱动程序管理器启动后,打开任何浏览器,在浏览器中输入 URL https://:4444/wd/hub,检查监听器是否已启动。
Using the Code
步骤 1:设置 Node.js 控制台应用程序
在主解决方案文件中添加新项目。
搜索 Nodejs 控制台应用程序模板并为其命名。
步骤 2:为 protractor 配置添加 conf.js 文件
作为示例,只需在名为“conf.js”的文件中添加以下代码:
// An example configuration file.
exports.config = {
// The address of a running selenium server.
seleniumAddress: 'https://:4444/wd/hub',
// Capabilities to be passed to the webdriver instance.
//capabilities: {
// 'browserName': 'chrome'
//},
multiCapabilities: [{
'browserName': 'chrome'
}, {
'browserName': 'firefox'
}],
// Spec patterns are relative to the current working directly when
// protractor is called.
specs: ['customConfig.js', 'menu.js', 'homePage.js' , 'candidatePage.js'],
// Options to be passed to Jasmine-node.
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000
}
};
请注意
使用 multiCapabilities 将在每种浏览器中运行所有测试。
步骤 3:构建项目并获取项目 URL
按 CTRL + F5 并记下应用程序的 localhost URL。
例如:https://:3472/#/
步骤 4:语法理解
Protractor API:http://angular.github.io/protractor/#/api
注意:大多数命令都会返回 Promise,因此您只能通过使用 jasmine expect API 或使用 .then(function()
) 结构来解析它们的值。
Protractor 备忘单
控制浏览器
browser.get('yoururl// Load address, can also use '#yourpage'
browser.navigate().back();
browser.navigate().forward();
browser.sleep(10000); // if your test is outrunning the browser
browser.pause();
browser.debugger();
browser.waitForAngular(); // if your test is outrunning the browser
browser.getLocationAbsUrl() // get the current address
检查可见性
element(by.id('create')).isPresent();
element(by.id('create')).isEnabled();
element(by.id('create')).isDisplayed();
按 id、model、binding 查找元素
element(by.id('user_name');
element(by.css('#myItem');
element(by.model('person.name');
element(by.binding('person.concatName'));
element(by.textarea('person.extraDetails'));
element(by.input('username'));
element(by.input('username')).clear();
element(by.buttonText('Save'));
element(by.partialButtonText('Save'));
element(by.linkText('Save'));
element(by.partialLinkText('Save'));
element(by.css('[ng-click="cancel()"]'));
var dog = element(by.cssContainingText('.pet', 'Dog'));
var allOptions = element.all(by.options('c c in colors'));
按 css、repeater、xpath 等查找元素集合。
var list = element.all(by.css('.items'));
var list2 = element.all(by.repeater('personhome.results'));
var list3 = element.all(by.xpath('//div');
expect(list.count()).toBe(3);
element(by.id('user_name')).sendKeys('user1');
sendKeys(protractor.Key.ENTER);
sendKeys(protractor.Key.TAB);
element(by.id('user_name')).clear();
element(by.id('item1')).getLocation().then(function(location) {
var x = location.x;
var y = location.y;
});
element(by.id('item1')).getSize().then(function(size) {
var width = size.width;
var height = size.height;
});
Protractor 概念
describe("A suite is just a function", function() {
var a;
it("and so is a spec", function() {
a = true;
expect(a).toBe(true);
});
});
-
describe 您的测试一个测试套件以调用全局 Protractor 函数 describe 开始,该函数有两个参数:一个字符串和一个函数。字符串是规格套件的名称或标题——通常是正在测试的内容。函数是实现该套件的代码块。SpecsSpecs 通过调用全局 Protractor 函数 it 来定义,该函数与 describe 一样,接受一个字符串和一个函数。字符串是规格的标题,函数是规格或测试。一个规格包含一个或多个用于测试代码状态的期望。Protractor 中的期望是一个布尔值为 true 或 false 的断言。所有期望都为 true 的规格是一个通过的规格。一个或多个期望为 false 的规格是一个失败的规格。
-
它们只是函数因为 describe 和 it 块是函数,它们可以包含实现测试所需的任何可执行代码。JavaScript 作用域规则适用,因此在 describe 中声明的变量在套件内的任何 it 块中都可用。
-
ExpectationsExpectations 是通过函数 expect 构建的,该函数接受一个值,称为实际值。它与一个匹配器函数链接,该函数接受期望值。
-
Matchers每个匹配器实现实际值和期望值之间的布尔比较。它负责向 Protractor 报告期望是 true 还是 false。Protractor 将会通过或失败该规格。任何匹配器都可以通过在调用匹配器之前链接 expect 的调用来评估为负断言。
-
Setup and Teardown为了帮助测试套件 DRY(不要重复自己)任何重复的设置和拆卸代码,Protractor 提供了全局
beforeEach
和afterEach
函数。顾名思义,beforeEach 函数在它所调用的 describe 中的每个规格之前调用一次,afterEach
函数在每个规格之后调用一次。下面是使用不同方式编写的相同规格集。被测试的变量在顶层作用域——describe 块——中定义,初始化代码移到了 beforeEach 函数中。afterEach 函数在继续之前重置变量。 -
waitForAngular() 指示 webdriver 在继续之前等待 Angular 完成渲染并且没有任何未完成的 $http 或 $timeout 调用。请注意,Protractor 会自动在每个 WebDriver 操作之前应用此命令。
-
sendKeys()
安排一个命令来键入/更新此实例所表示的 DOM 元素的值。我知道这有点难理解,这里有一个简单的解释:“将一个或多个按键发送到活动窗口,就像它们是通过键盘输入一样”。例如,为了输入任何文本输入框的值,我们使用 sendKeys() 函数。
步骤 5a:为 e2e 测试添加第一个测试规格文件
作为示例,只需在名为“menu.js”的文件中添加以下代码:
describe('menu check', function () {
beforeEach(function () {
browser.get('https://:3472/#/');
browser.waitForAngular();
});
it('Should route to the candidates page from menu', function () {
element(by.linkText('Users')).click();
expect(browser.getCurrentUrl()).toMatch('https://:3472/#/candidates');
});
it('Should route to the home page from menu', function () {
element(by.linkText('Home')).click();
expect(browser.getCurrentUrl()).toMatch('https://:3472/#/');
});
});
此文件中的测试将单击菜单项,并确认其更新后的 URL 与测试用例的预期值匹配,即 Home 和 Users。
将步骤 3 中的 URL 复制到 menu.js 文件中的 browser.get 语句中。文件名应与您在 conf.js 中 specs 节点下定义的文件名相似。
// An example configuration file.
exports.config = {
// Spec patterns are relative to the current working directly when
// protractor is called.
specs: ['menu.js'],
};
步骤 5b:为 e2e 测试添加第二个测试规格文件
作为示例,只需在名为“homePage.js”的文件中添加以下代码:
describe('Testing Homepage', function () {
beforeEach(function () {
browser.get('https://:3472/#/');
browser.waitForAngular();
});
it('should have a title', function () {
expect(browser.getTitle()).toEqual('TAC Sample Angular Utility');
});
it('shaould have header of page', function () {
var header = element(by.binding('type'));
expect(header.getText()).toMatch('Admin Page');
});
it('Should update the url', function () {
expect(browser.getCurrentUrl()).toMatch('https://:3472/#/');
});
it('Should route to the candidates page', function () {
element(by.linkText('candidates')).click();
expect(browser.getCurrentUrl()).toMatch('https://:3472/#/candidates');
});
it('Should search and return rows', function () {
var name = element(by.model('search.first_name'));
name.sendKeys('jack');
browser.waitForAngular();
var candidates = element.all(by.repeater('candidate in vm.candidates'));
expect(candidates.count()).toBe(1);
});
});
此文件中的测试将检查页面标题、页面标题、URL、单击 href 链接以及搜索结果的数量,如下图所示。
文件名应与您在 conf.js 中 specs 节点下定义的文件名相似。
// An example configuration file.
exports.config = {
// Spec patterns are relative to the current working directly when
// protractor is called.
specs: ['menu.js', 'homePage.js'],
};
步骤 5c:为 e2e 测试添加第二个测试规格文件
作为示例,只需在名为“candidatePage.js”的文件中添加以下代码:
describe('Testing Candidate Page', function () {
beforeEach(function () {
browser.get('https://:3472/#/candidates');
browser.waitForAngular();
});
it('Should add candidates and return rows', function () {
element(by.model('vm.newCandidate.first_name')).sendKeys('aaq');
element(by.model('vm.newCandidate.middle_initial')).sendKeys('aa');
element(by.model('vm.newCandidate.last_name')).sendKeys('aac');
element(by.model('vm.newCandidate.email')).sendKeys('haa@aa.com');
element(by.model('vm.newCandidate.expected_salary')).sendKeys('234');
element(by.css('.btn-primary')).click();
browser.waitForAngular();
var candidates = element.all(by.repeater('candidate in vm.candidates'));
expect(candidates.count()).toBe(11);
});
it('Should invalidate email', function () {
element(by.model('vm.newCandidate.email')).sendKeys('haa@com');
element(by.model('vm.newCandidate.expected_salary')).sendKeys('111');
browser.waitForAngular();
element(by.model('vm.newCandidate.email')).getCssValue('background-color').then(function (color) {
expect(color).toEqual('rgba(255, 255, 0, 1)');
});
});
it('Should add edit candidates', function () {
element.all(by.repeater('candidate in vm.candidates')).then(function (candidates) {
candidates[0].element(by.buttonText('Edit')).click();
candidates[0].element(by.model('vm.itemToEdit.first_name')).clear().then(function () {
candidates[0].element(by.model('vm.itemToEdit.first_name')).sendKeys('BillBBP');
});
candidates[0].element(by.model('vm.itemToEdit.middle_initial')).clear().then(function () {
candidates[0].element(by.model('vm.itemToEdit.middle_initial')).sendKeys('BBP');
});
candidates[0].element(by.model('vm.itemToEdit.last_name')).clear().then(function () {
candidates[0].element(by.model('vm.itemToEdit.last_name')).sendKeys('GatesBBP');
});
candidates[0].element(by.model('vm.itemToEdit.email')).clear().then(function () {
candidates[0].element(by.model('vm.itemToEdit.email')).sendKeys('BbP@aa.com');
});
candidates[0].element(by.model('vm.itemToEdit.expected_salary')).clear().then(function () {
candidates[0].element(by.model('vm.itemToEdit.expected_salary')).sendKeys('800');
});
candidates[0].element(by.buttonText('Save')).click();
});
browser.waitForAngular();
element.all(by.repeater('candidate in vm.candidates')).then(function (candidates) {
var fName = candidates[0].element(by.binding('candidate.first_name'));
expect(fName.getText()).toEqual('BillBBP');
});
});
});
此文件中的测试将向网格中添加候选记录,如下图所示。
此文件中的测试将检查在添加记录期间无效的电子邮件值,如下图所示。
此文件中的测试将编辑网格中的第一个候选记录,如下图所示。
文件名应与您在 conf.js 中 specs 节点下定义的文件名相似。
// An example configuration file.
exports.config = {
// Spec patterns are relative to the current working directly when
// protractor is called.
specs: ['menu.js', 'homePage.js', 'candidatePage.js'],
};
步骤 6(可选):降低 Protractor 测试的速度
您可能会注意到这些测试执行速度很快,浏览器会被迅速调用,并且这些测试会在几秒钟内完成。为了降低速度,请按照以下步骤操作:
作为示例,只需在名为“customConfig.js”的文件中添加以下代码:
var origFn = browser.driver.controlFlow().execute;
browser.driver.controlFlow().execute = function () {
var args = arguments;
origFn.call(browser.driver.controlFlow(), function () {
//increase or reduce time value, its in millisecond
return protractor.promise.delayed(100);
});
return origFn.apply(browser.driver.controlFlow(), args);
};
文件名应与您在 conf.js 中 specs 节点下定义的文件名相似。
// An example configuration file.
exports.config = {
// Spec patterns are relative to the current working directly when
// protractor is called.
specs: ['customConfig.js', 'menu.js', 'homePage.js', 'candidatePage.js']
};
步骤 7:运行测试规格
右键单击项目并选择“在此处打开命令提示符”(我们也可以在 CMD 中执行此操作,但目前先留在 VS 环境)。运行 protractor 示例规格文件,请执行以下命令:
protractor conf.js
CMD 后面的 Chrome 和 Firefox 浏览器由 Selenium WebDriver 启动,并执行我们的测试,输出将在 CMD 中生成,如下所示:
Protractor 执行这些测试的视频如下:
关注点
安装 protractor 时可能会出现一些错误,例如缺少依赖项,例如缺少 Python,请按照我的博客文章进行操作。
- 错误:Protractor 2.0 在 npm install 期间由于依赖项而出错——找不到 Python 可执行文件
- 在不同浏览器上并行执行 Protractor 测试
- 降低 Angular E2E Protractor 测试的速度
- Bower 错误 - fatal: unable to connect to github.com
完整的源代码请参阅: