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

时间:2013-11-15 09:49:45

标签: protractor

刚开始使用Protractor进行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
            }
        });
    });
});

3 个答案:

答案 0 :(得分:11)

我来到这个问题寻找一种在Protractor中的spec文件之间共享辅助函数的方法。如果其他人正在寻找相同的东西,那么因为Protractor刚刚在Node中运行,所以你需要做的只是var helpers = require('./your-helper-file')

答案 1 :(得分:7)

如果您需要共享设置以及函数之前/之后以及辅助方法,一种解决方案是要求您的规范帮助程序进行测试,而不是要求测试中的规范帮助程序。

<强> conf.js

exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['e2e/spec.js']
}

<强> 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()

<强> E2E / homepage.js

exports.test = function() {
  describe('homepage', function() {
    it('should have the right title', function() {
      browser.get('/')

      browser.getTitle().then(function(title){
        title.should.eq('Home')
      })
    });
  });
}

<强> E2E / signin.js

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')
      })
    });
  });
}

答案 2 :(得分:2)

我自己也在看同样的事情,在某种程度上我希望你能在这个问题上找到答案。 : - )

话虽如此,看来量角器是新的,没有人真正知道答案,我想这使我的答案和下一个人一样好。

首先,我正在使用量角器入门页面上描述的页面对象表示法,在底部:https://github.com/angular/protractor/blob/master/docs/getting-started.md

这提供了一个模块化元素,我的观点是我最终得到一组类,每页一个,抽象出一些细节。因此,例如,我可能有一个“foo”类,其中包括“foo.get”和“foo.validate(id,name,otherData)”等抽象。这将是一种拉出重复代码的方法。

我没有解决的问题是如何创建模块库,然后将它们组装成一组场景。我有几点想法:

  1. 潜在的问题是能够将javascript文件包含在彼此中 - 这实际上并不作为一种功能存在。有第三方库,我不想使用它,我还没有看到使用Angular的模块功能来实现这一点的方法。
  2. 结束2结束测试可能非常依赖于测试的顺序。因此,一个测试可以创建数据,然后另一个测试可以使用该数据。例如,如果您想要一个记录人员的测试,您可能需要先测试人员的测试。您可能不希望将注册放在您运行的每个测试的前面。因此,您可能需要对测试场景的顺序进行大量控制
  3. 因此,一种选择是将所有内容放在一个非常大的文件中。这违背了我们在学校学到的一切,但我并没有真正想出一个不起作用的理由。然后你可以写出你内心的功能和抽象。
  4. 如果您按照下一步进行操作,另一个选择是编写一系列具有严格命名约定的javascript文件,然后在执行它们之前使用grunt为它们连接它们。所以,例如:
    • 一组名为xxxx.page.scenario.js的文件,其中包含“页面对象”定义 - 基本上是每个页面的帮助方法
    • 一组名为xxxx.functions.scenario.js的文件,其中包含场景的常见组件 - 因此,您可能拥有一个注册和登录操作集,并将其转换为库函数
    • 一组名为nnxx.scenarios.scenario.js的文件,其中包含实际脚本本身。这些在开始时编号(nn),因此我们可以以可靠的顺序连接它们,从而控制脚本运行的顺序
  5. 我还没有说这是一个好主意,只是表面看起来它可以起作用,并且会产生预期的结果。我主要担心的是它感觉很脆弱 - 因此随着测试套件的大小增加,它可能变得非常难以维护。也许另一种方法是,不是对场景进行编号,而是将它们定义为依赖项,并且有一些东西可以确保任何给定的脚本在它声明自己依赖的任何脚本之后运行。这也许允许对脚本进行子集化 - 所以你可以说“运行吧脚本”,框架会知道条形脚本需要先运行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,如果你想要上下文:{ {3}}