web-dev-qa-db-ja.com

テストを個別の仕様に分割してから別の仕様から呼び出すことはできますか、それともヘルパー関数を使用する方がよいですか?

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
            }
        });
    });
});
19
Annie

分度器のスペックファイル間でヘルパー関数を共有する方法を探してこの質問に行きました。他の人が同じものを探している場合、ProtractorはNodeで実行されているだけなので、必要なのはvar helpers = require('./your-helper-file')だけです。

11
iangilman

@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();
        });
  });

}());
14
WeMakeSoftware

共有セットアップと前後の関数、およびヘルパーメソッドが必要な場合、1つの解決策は、テストからスペックヘルパーを要求するのではなく、スペックヘルパーからテストを要求することです。

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

私自身も同じことを考えていますが、この質問に対する回答があれば、ある程度期待していました。 :-)

そうは言っても、分度器は十分に新しいので、誰も答えを本当に知らないようです。それが私の答えを次の人と同じくらい良くしていると思います。

まず、分度器の開始ページの下部に記載されているページオブジェクト表記を使用しています: https://github.com/angular/protractor/blob/master/docs/getting-started。 md

これはモジュール性の要素を与えます。ここでの私の見解は、ページごとに1つずつ、詳細の一部を抽象化するクラスのセットになってしまうというものです。したがって、たとえば、「foo.get」や「foo.validate(id、name、otherData)」などの抽象化を含む「foo」クラスがあるとします。これは、繰り返されるコードを引き出す方法です。

私がまだ理解していないのは、モジュールのライブラリを作成し、それらを単一のシナリオセットにアセンブルする方法です。しかし、私はいくつかの考えを持っています:

  1. 根本的な問題は、JavaScriptファイルを相互に含めることができることです。これは実際には機能として存在しません。使用したくないサードパーティのライブラリがありますが、Angularのモジュール機能を使用してこれを行う方法は見たことがありません。
  2. エンド2エンドテストは、テストの順序に大きく依存する可能性があります。したがって、あるテストでデータが作成され、別のテストでそのデータが使用される場合があります。例として、人をログオンさせるテストが必要な場合は、最初に人を登録するテストが必要になる場合があります。実行するすべてのテストの前に登録を配置したくない場合があります。そのため、とにかくテストシナリオの順序を細かく制御する必要があります。
  3. そのため、1つのオプションは、すべてを1つの非常に大きなファイルに入れることです。これは私たち全員が学校で学んだことすべてに反しますが、私は実際にはうまくいかない理由を思いつきませんでした。次に、心のコンテンツに関数と抽象化を書くことができます。
  4. それを次の段階に進める場合、別のオプションは、厳密な命名規則を使用して一連のjavascriptファイルを作成し、実行する前にgruntを使用してそれらを連結することです。したがって、たとえば:
    • 「ページオブジェクト」定義を含むxxxx.page.scenario.jsという名前のファイルのセット-基本的に各ページのヘルパーメソッド
    • シナリオの一般的なコンポーネントを含むxxxx.functions.scenario.jsという名前のファイルのセット-したがって、レジスターとログオンの一連のアクションがあり、それをライブラリー関数にします。
    • Nnxx.scenarios.scenario.jsという名前のファイルのセット。これには、実際のスクリプト自体が含まれています。これらは最初(nn)に番号が付けられているため、信頼できる順序で連結して、スクリプトの実行順序を制御できます。

これが良い考えだとはまだ言っていませんが、少なくとも表面的にはうまくいくように見え、望ましい結果が得られるというだけです。私の主な懸念は、それが壊れやすいと感じることです。そのため、テストスイートのサイズが大きくなると、保守が非常に困難になる可能性があります。おそらく、これを行う別の方法は、シナリオに番号を付ける代わりに、シナリオを依存関係として定義し、依存していると宣言したスクリプトの後に特定のスクリプトが実行されるようにすることです。これにより、スクリプトのサブセット化も可能になる可能性があります。つまり、「barスクリプトを実行する」と言うと、フレームワークは、barスクリプトで最初にfooスクリプトを実行し、ログインスクリプトを実行する必要があることを認識します。ただし、他のすべてのスクリプトを省略してもかまいません。

編集:私はここでアストロラーベが潜在的に良い答えだと思います、それはあなたがあなたのテストをモジュール化することを明示的に可能にするようです。 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/

2
PaulL