Каков правильный синтаксис транспортира для объектов страницы?

Я сталкивался с различными типами синтаксиса для объектов страницы Protractor, и мне было интересно, какова их предыстория и какой способ предлагается.

Это официальный синтаксис PageObject из руководства по Protractor. Мне он нравится больше всего, потому что он понятен и читабелен:

use strict;

var AngularHomepage = function() {
  var nameInput = element(by.model('yourName'));
  var greeting = element(by.binding('yourName'));

  this.get = function() {
    browser.get('http://www.angularjs.org');
  };

  this.setName = function(name) {
    nameInput.sendKeys(name);
  };

  this.getGreeting = function() {
    return greeting.getText();
  };
};
module.exports = AngularHomepage;

Однако я также нашел такой вид:

'use strict';

var AngularPage = function () {
  browser.get('http://www.angularjs.org');
};

    AngularPage.prototype  = Object.create({}, {
      todoText:  {   get: function ()     { return element(by.model('todoText'));             }},
      addButton: {   get: function ()     { return element(by.css('[value="add"]'));          }},
      yourName:  {   get: function ()     { return element(by.model('yourName'));             }},
      greeting:  {   get: function ()     { return element(by.binding('yourName')).getText(); }},
      todoList:  {   get: function ()     { return element.all(by.repeater('todo in todos')); }},
      typeName:  { value: function (keys) { return this.yourName.sendKeys(keys);              }} ,
      todoAt:    { value: function (idx)  { return this.todoList.get(idx).getText();          }},
      addTodo:   { value: function (todo) {
        this.todoText.sendKeys(todo);
        this.addButton.click();
      }}
    });

    module.exports = AngularPage;

Каковы плюсы/минусы этих двух подходов (помимо удобочитаемости)? Второй актуален? Я видел, что WebdriverIO использует этот формат.

Я также слышал от одного парня на Gitter, что первая запись неэффективна. Может кто-нибудь объяснить мне, почему?


person anks    schedule 03.08.2016    source источник


Ответы (2)


Фреймворк Page Object Model становится популярным в основном из-за:

  1. Меньше дублирования кода
  2. Легко поддерживать в течение длительного времени
  3. Высокая читаемость

Таким образом, обычно мы разрабатываем тестовую среду (pom) для нашего удобства, основываясь на объеме и потребностях тестирования, следуя подходящим шаблонам структуры (pom). НЕТ таких правил, которые говорят, что мы должны строго следовать каким-то рамкам.

ПРИМЕЧАНИЕ. Структура предназначена для того, чтобы сделать нашу задачу простой, ориентированной на результат и эффективной.

В вашем случае первый выглядит хорошо и легко. И это не приводит к путанице или конфликту на этапе обслуживания.

Пример: 1-й случай-> объявление локатора элемента происходит вверху каждой страницы. Было бы легко изменить, если бы какой-либо локатор элемента изменился в будущем.

В то время как во 2-м случае локаторы объявлены на уровне блоков (разбросаны по странице). Процесс определения и изменения локаторов, если потребуется в будущем, займет много времени.

Итак, выберите тот, который вам удобен, исходя из вышеперечисленных пунктов.

person Optimworks    schedule 03.08.2016
comment
Большое спасибо, это имеет смысл :) - person anks; 05.08.2016
comment
@anks, если вы чувствуете, что мой пост поможет вам и исправьте, пожалуйста, отметьте его как правильный. - person Optimworks; 06.08.2016

Я предпочитаю использовать синтаксис класса ES6 (http://es6-features.org/#ClassDefinition) . Здесь я подготовил несколько простых примеров того, как я работаю с объектами страницы, используя классы ES6, и некоторые полезные приемы.

var Page = require('../Page')
var Fragment = require('../Fragment')

class LoginPage extends Page {
    constructor() {
        super('/login');
        this.emailField = $('input.email');
        this.passwordField = $('input.password');
        this.submitButton = $('button.login');

        this.restorePasswordButton = $('button.restore');
    }

    login(username, password) {
        this.email.sendKeys(username);
        this.passwordField.sendKeys(password);
        this.submit.click();
    }

    restorePassword(email) {
        this.restorePasswordButton.click();
        new RestorePasswordModalWindow().submitEmail(email);
    }
}

class RestorePasswordModalWindow extends Fragment {
    constructor() {
        //Passing element that will be used as this.fragment;
        super($('div.modal'));
    }

    submitEmail(email) {
        //This how you can use methods from super class, just example - it is not perfect.
        this.waitUntilAppear(2000, 'Popup should appear before manipulating');
        //I love to use fragments, because they provides small and reusable parts of page.
        this.fragment.$('input.email').sendKeys(email);
        this.fragment.$('button.submit')click();
        this.waitUntilDisappear(2000, 'Popup should disappear before manipulating');
    }
}
module.exports = LoginPage;

// Page.js
class Page {
    constructor(url){
        //this will be part of page to add to base URL.
        this.url = url;
    }

    open() {
        //getting baseURL from params object in config.
        browser.get(browser.params.baseURL + this.url);
        return this; // this will allow chaining methods.
    }
}
module.exports = Page;

// Fragment.js
class Fragment {
    constructor(fragment) {
        this.fragment = fragment;
    }

    //Example of some general methods for all fragments. Notice that default method parameters will work only in node.js 6.x
    waitUntilAppear(timeout=5000, message) {
        browser.wait(this.EC.visibilityOf(this.fragment), timeout, message);
    }

    waitUntilDisappear(timeout=5000, message) {
        browser.wait(this.EC.invisibilityOf(this.fragment), timeout, message);
    }
}
module.exports = Fragment;

// Then in your test:
let loginPage = new LoginPage().open(); //chaining in action - getting LoginPage instance in return.
loginPage.restorePassword('[email protected]'); // all logic is hidden in Fragment object
loginPage.login('[email protected]')
person Xotabu4    schedule 03.08.2016
comment
После того, как я опубликовал это, мой друг также посоветовал мне использовать ES6, и я согласен, что это выглядит очень красиво и более знакомо для меня (раньше я писал объекты страницы на Ruby). Однако один вопрос - вы не используете синтаксис Protractor для элементов (например, element(by.css('someclass')) - это потому, что $('') короче или по другим причинам? - person anks; 05.08.2016
comment
Да, я часто использую element(by.css('')) и $/$$ - хороший глобальный ярлык для него. Никакой функциональной разницы. - person Xotabu4; 06.08.2016
comment
этот не работает для меня. Я пытался использовать класс javascript и в конце использовать module.exports = ClassName; В моем test.js я использую var Ahomepage = require('./AngularHomePagePOM.js'); Домашняя страница = новая POM_HomePage(); но всегда получаю ошибку undefined [17:53:41] E/launcher - Ошибка: ReferenceError: POM_HomePage не определен - person Francis Saul; 24.03.2020