【问题标题】:Java Page Object Model Best Practices - New ObjectsJava 页面对象模型最佳实践 - 新对象
【发布时间】:2017-01-10 15:46:40
【问题描述】:

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

或者,在 LoginSteps 扩展的“baseSteps”类中实例化所有页面是否更有效?

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

【问题讨论】:

    标签: java automation cucumber pageobjects


    【解决方案1】:

    我会进行基准测试,看看哪个版本最有效。

    Cucumber 代码的编译方式取决于您的构建工具。我会假设一切都是在使用之前编译的。但是,在某些执行后进行自我优化的 JVM 可能会在一段时间后表现不同。

    我真的很想知道您为什么要考虑这个级别的性能?您是否希望一次执行此代码数小时?我的大多数场景都很短,在第二个规模上执行,然后就扔掉了。

    【讨论】:

    • 我会试一试,看看是否能找到任何不同之处。这样做的原因是因为我将持续运行这些,我需要它们尽可能高效。感谢您的帮助。
    【解决方案2】:

    您的 Cucumber 测试是否针对 GUI 运行?如果是这样,无论您是创建给定页面对象的单个实例还是多个实例,都不会产生影响。这种测试本质上是缓慢的,这样的调整是难以察觉的。

    无论是哪种情况,都应该优先考虑一个好的设计。除了简化代码的未来维护之外,好的设计还可以让您改进系统性能。但是,反过来就不是这样了。

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

    至于 Cucumber 层,我认为如下(同样,不再赘述):

    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 层显式创建的唯一页面对象。

    【讨论】:

      猜你喜欢
      • 2011-12-21
      • 1970-01-01
      • 2022-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      • 1970-01-01
      • 2011-01-25
      相关资源
      最近更新 更多