65.9K
CodeProject 正在变化。 阅读更多。
Home

AngularJS 应用程序的 Protractor 简介

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (8投票s)

2015 年 12 月 21 日

CPOL

7分钟阅读

viewsIcon

30812

在 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

步骤 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 将在每种浏览器中运行所有测试。

https://hassantariqblog.wordpress.com/2015/11/09/execute-protractor-test-in-parallel-on-different-browser/

步骤 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 提供了全局 beforeEachafterEach 函数。顾名思义,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,请按照我的博客文章进行操作。

完整的源代码请参阅:

© . All rights reserved.