Каков наилучший подход к созданию экземпляров объектов на этапах создания объектной модели страницы Java?

Кто-нибудь знает, как компилируются скрипты Cucumber?

Я предполагаю, что если все будет построено и выполнено, 2-й или 3-й вариант ниже может быть лучшим подходом.

Если бы были скомпилированы только шаги, связанные с выполняемым тестом, я бы предположил, что это будет первым.

У меня есть такой пример:

private LoginPage loginPage = new LoginPage(driver);

@Given("^I have logged in as customer with two stored cards$")
public void iHaveLoggedInAsCustomerWithTwoStoredCards() throws Throwable {
    new HomePage(driver).clickLoginButton();
    loginPage.enterEmail("test@test.com");
    loginPage.enterPassword("Password1");
    loginPage.clickLogin();        
}

@Given("^I have logged in as customer with expired card$")
public void iHaveLoggedInAsCustomerWithExpiredCard() throws Throwable {
    new HomePage(driver).clickLoginButton();
    loginPage.enterEmail("test02@test.com");
    loginPage.enterPassword("Password1");
    loginPage.clickLogin();        
}

@Given("^the user logged in as customer with three stored cards$")
public void theUserLoggedInAsCustomerWithThreeStoredCards() throws Throwable {
    new HomePage(driver).clickLoginButton();
    loginPage.enterEmail("test03@test.com");
    loginPage.enterPassword("Password1");
    loginPage.clickLogin();        
}

Все вышеперечисленные шаги (плюс другие в том же классе LoginSteps.java) начинаются с

new HomePage(driver).clickLoginButton();

Это лучший подход или лучше создать один экземпляр?

private LoginPage loginPage = new LoginPage(driver);
private HomePage homePage = new HomePage(driver);

@Given("^I have logged in as customer with two stored cards$")
public void iHaveLoggedInAsCustomerWithTwoStoredCards() throws Throwable {
    homePage.clickLoginButton();
    loginPage.enterEmail("test@test.com");
    loginPage.enterPassword("Password1");
    loginPage.clickLogin();        
}

@Given("^I have logged in as customer with expired card$")
public void iHaveLoggedInAsCustomerWithExpiredCard() throws Throwable {
    homePage.clickLoginButton();
    loginPage.enterEmail("test02@test.com");
    loginPage.enterPassword("Password1");
    loginPage.clickLogin();        
}

@Given("^the user logged in as customer with three stored cards$")
public void theUserLoggedInAsCustomerWithThreeStoredCards() throws Throwable {
    homePage.clickLoginButton();
    loginPage.enterEmail("test03@test.com");
    loginPage.enterPassword("Password1");
    loginPage.clickLogin();        
}

ИЛИ еще эффективнее создать экземпляры всех страниц в классе baseSteps, который расширяет LoginSteps?

public class LoginSteps extends BaseSteps {
    @Given("^I have logged in as customer with two stored cards$")
    public void iHaveLoggedInAsCustomerWithTwoStoredCards() throws Throwable {
        homePage.clickLoginButton();
        loginPage.enterEmail("test@test.com");
        loginPage.enterPassword("Password1");
        loginPage.clickLogin();        
    }

    @Given("^I have logged in as customer with expired card$")
    public void iHaveLoggedInAsCustomerWithExpiredCard() throws Throwable {
        homePage.clickLoginButton();
        loginPage.enterEmail("test02@test.com");
        loginPage.enterPassword("Password1");
        loginPage.clickLogin();        
    }

    @Given("^the user logged in as customer with three stored cards$")
    public void theUserLoggedInAsCustomerWithThreeStoredCards() throws Throwable {
        homePage.clickLoginButton();
        loginPage.enterEmail("test03@test.com");
        loginPage.enterPassword("Password1");
        loginPage.clickLogin();        
    }
}

public baseSteps {

    protected LoginPage loginPage = new LoginPage(driver);
    protected HomePage homePage = new HomePage(driver);
}
2
Andrew Evans 10 Янв 2017 в 18:46

2 ответа

Я бы проверил, какая версия наиболее эффективна.

Как компилируется код Cucumber, зависит от вашего инструмента сборки. Я предполагаю, что все компилируется перед использованием. Но JVM, которая оптимизируется после некоторого выполнения, через некоторое время может вести себя иначе.

Мне действительно интересно услышать, почему вы думаете о производительности на этом уровне? Ожидаете ли вы, что этот код будет выполняться часами? Большинство моих сценариев короткие, выполняются во втором масштабе, а затем выбрасываются.

1
Thomas Sundberg 10 Янв 2017 в 21:55
Я попробую и посмотрю, смогу ли я найти какую-нибудь разницу. Причина этого в том, что я собираюсь использовать их на постоянной основе, и мне нужно, чтобы они были как можно более эффективными. Спасибо за вашу помощь.
 – 
Andrew Evans
11 Янв 2017 в 16:57

Ваши тесты Cucumber работают с графическим интерфейсом? Если это так, не будет иметь значения, создаете ли вы один или несколько экземпляров данного объекта страницы. Такого рода тесты медленные по своей природе, и такая настройка будет незаметной.

Как бы то ни было, в приоритете всегда должен быть хороший дизайн. Помимо упрощения обслуживания кода в будущем, хороший дизайн позволит вам улучшить производительность системы. Однако наоборот.

Огурец и объектная модель страницы очень хорошо сочетаются друг с другом. Но они должны быть расположены в отдельных слоях, причем Cucumber находится поверх объектной модели страницы, выступая в качестве клиента последней. Наблюдая за вашими примерами кода, я не понимаю, почему объекты страницы создаются как часть кода определений шагов. Почему сам метод clickLoginButton не возвращает экземпляр LoginPage?

На мой взгляд, объектная модель страницы должна поддерживаться двумя основными принципами:
1. API . Каждый объект страницы должен определять API с методами, соответствующими действиям, которые пользователь может выполнять на этой странице.
2. Навигация . Каждое действие должно переводить пользователя либо на ту же страницу, либо на другую. Таким образом, каждый метод должен возвращать либо сам объект страницы, либо другой объект страницы.

Первый выполняется вашим кодом, а второй - нет, или, по крайней мере, он недостаточно ясен.

Я бы подумал о классах HomePage и LoginPage как о чем-то вроде этого (не вдаваясь в подробности):

public class HomePage
{
    public HomePage(WebDriver driver) {
        // ...
    }

    public LoginPage clickLoginButton() {
        perform click login button
        return new LoginPage(this.driver);
    }
}

public class LoginPage
{
    public LoginPage(WebDriver driver) {
        // ...
    }

    public LoginPage enterEmail(String email) {
        perform enter email
        return this;
    }

    public LoginPage enterPassword(String password) {
        perform enter password
        return this;
    }

    public NextPage clickLogin() {
        perform click login
        return new NextPage();
    }
}

Что касается слоя с огурцом, я бы подумал о нем следующим образом (опять же, не вдаваясь в подробности):

private static final String PASSWORD = "Password1";
private static final String EMAIL_CUSTOMER_WITH_TWO_STORED_CARDS = "test@test.com";
private static final String EMAIL_CUSTOMER_WITH_EXPIRED_CARD = "test02@test.com";
private static final String EMAIL_CUSTOMER_WITH_THREE_STORED_CARDS = "test03@test.com";

@Given("^I have logged in as customer with two stored cards$")
public void iHaveLoggedInAsCustomerWithTwoStoredCards() throws Throwable {
    this.performLogin(EMAIL_CUSTOMER_WITH_TWO_STORED_CARDS);
}

@Given("^I have logged in as customer with expired card$")
public void iHaveLoggedInAsCustomerWithExpiredCard() throws Throwable {
    this.performLogin(EMAIL_CUSTOMER_WITH_EXPIRED_CARD);
}

@Given("^the user logged in as customer with three stored cards$")
public void theUserLoggedInAsCustomerWithThreeStoredCards() throws Throwable {
    this.performLogin(EMAIL_CUSTOMER_WITH_THREE_STORED_CARDS);
}

private void performLogin(String email) {
    nextPage = homePage.clickLoginButton()
        .enterEmail(email)
        .enterPassword(PASSWORD)
        .clickLogin();
}

См. (Необязательно) цепочку методов в последнем методе как следствие объектов страницы, возвращаемых методами объектной модели страницы.

Я намеренно пропустил объявление переменных homePage, loginPage и nextPage. LoginSteps не разделяет переменные с остальными классами Steps, даже если вы заставляете их расширять общий класс BaseSteps, если вы не объявили их как статические:

public BaseSteps {
    protected static HomePage homePage;
    protected static LoginPage loginPage;
    protected static NextPage nextPage;
    ...
}

В качестве альтернативы вы могли бы рассмотреть возможность интеграции инфраструктуры внедрения зависимостей, но я не пробовал.

Сохранение переменных в качестве атрибутов частного экземпляра не будет работать (даже если они не используются совместно с другими классами Steps), потому что Cucumber создает экземпляр класса Steps для каждого тестового сценария, используя определение шага, содержащееся в классе.

Наконец, где-то в слое Cucumber должен быть перехватчик для инициализации точки входа в объектную модель страницы.

@Before
public void setUp() {
    homePage = new HomePage(driver);
}

Это должен быть единственный объект страницы, явно созданный из слоя Cucumber.

1
Lazarus 14 Мар 2018 в 11:29