Protractor 我可以将我的测试划分为单独的规范,然后从另一个规范调用它们吗?还是使用辅助函数更好?

Protractor 我可以将我的测试划分为单独的规范,然后从另一个规范调用它们吗?还是使用辅助函数更好?,protractor,Protractor,刚刚开始E2E测试的量角器,我在测试用例结构方面遇到了一些问题 不确定我是否可以将测试划分为单独的规范,然后从另一个规范调用它们,或者我如何制作好的助手函数来处理这个问题 我通过中继器查找元素,然后我想对中继器中每个元素的每个操作进行测试。有点像这样: describe('tasty', function () { 'use strict'; var ptor; beforeEach(function () { ptor = protractor.get

刚刚开始E2E测试的量角器,我在测试用例结构方面遇到了一些问题

不确定我是否可以将测试划分为单独的规范,然后从另一个规范调用它们,或者我如何制作好的助手函数来处理这个问题

我通过中继器查找元素,然后我想对中继器中每个元素的每个操作进行测试。有点像这样:

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