【问题标题】:Page Object Model Best Practices in SeleniumSelenium 中的页面对象模型最佳实践
【发布时间】:2011-12-21 07:44:44
【问题描述】:

当您对页面对象进行建模时,您将如何处理包含表单和大约 50 个输入字段的页面?这里的最佳做法是什么?

您会创建一个页面对象并为每个输入操作编写一个单独的函数吗?或者你会写一个函数,将参数传递给它并输入文本?

例如

public void enterFirstName(String firstName) {
    driver.type("firstNameField", firstName);
}

public void enterSecondName(String secondName) {
    driver.type("secondNameField", secondName);
}

public void fillInForm(String inputFieldName, String text) {
    driver.type(inputFieldName, text);
}

我可以在第一个模型中看到,在编写测试时,测试更具描述性,但是如果页面包含太多输入字段,创建页面对象变得很麻烦。

这篇文章在页面对象中构建硒测试也很有趣 Functional Automated Testing Best Practices with Selenium WebDriver

【问题讨论】:

  • 查看my stackoverflow question 以了解我将如何使用页面对象设计模式的示例。我自己不是 100% 自己,但通过大量阅读,我确信我的思路是正确的,希望我粘贴的内容对您有所帮助。
  • 您可以将所有相似的 WebElement 类型放入一个列表中。所以,如果你有 40 个文本字段,你可以从列表中一一绘制,获取“name”或“id”来识别它,然后对其进行操作。
  • 在设计页面对象时遵循单一职责原则 - 您将获得可读性和可靠的测试 - 更多信息:testautomationguru.com/arquillian-graphene-page-fragments

标签: java object selenium webdriver pageobjects


【解决方案1】:

页面对象模型背后的想法是将实现从调用者那里抽象出来。在第一种机制中,您成功地做到了这一点,因为调用者不需要知道 html 输入字段名称是否从“firstName”更改为“user_first_name”,而在您的第二个实现中,对实际页面的任何更改都必须是向您的页面对象的所有调用者发送。

虽然创建页面对象可能需要更多的前期工作,但如果您维护封装,从长远来看,当真正的 html 页面不可避免地发生变化时,它会节省工作。

【讨论】:

  • 感谢 digitalJoel,如何使用在页面顶部定义的公共定位器变量(如何)创建页面,然后在我的测试类中,实例化该页面并将定位器变量传递给通用的fillInform。从这个意义上说,我的变量仍然在一个位置,即页面对象,并且我所有的测试都会引用这些变量,所以当它们发生变化时,我仍然必须保持在一个位置。
  • 这是一种方法。将名称作为公共静态最终字段,然后提供一种方法。这一直有效,直到您需要对单个值进行一些特殊处理,然后您有一个单独的方法用于 myPage.setSpecialValue( blah ) 并且对于您拥有的其他所有东西 myPage.fillInForm(MyPage.MY_NAME, blah ) 并且它是不一致的,但这可能是您可以在何时/如果跨越的桥梁你来了,而不是现在必须为它建造。这取决于你。
  • 最好的“感谢”是对一些答案进行投票。在您的问题的 14 个答案中,您只有 4 个答案投了赞成票,没有接受任何答案。我不是在谈论我的答案,而是任何你认为有帮助的答案(比如 Sam Woods 在这个问题上的答案)都可以/应该被投票。
【解决方案2】:

我在表单中执行此操作的方式是获取页面上所有输入的列表。然后删除所有未显示的输入元素。之后,我可以将有效或无效的文本放入每个输入。从那里我捕获验证摘要以确保我得到正确的错误。如果没有,则记录异常。

它的作用是让我可以在页面上的任意数量的输入中输入文本,它仍然允许我记录异常并通过电子邮件发送它们。我还在我的列表中捕获了文本区域和密码字段,并且我有一个单独的复选框字段和选项列表,因为我通常想要对它们做不同的事情。

归结为,我要测试一个页面所要做的就是:

for (int i = 0; i < inputs.Count(); i++)
{
  //This captures the error message string created in the input validation method
  //nextButton is the IWebElement of the button to click to submit the form for validation
  //ErrorMessageID is the ID of the Validation Summary display box (i.e. ErrorMessageID = "FormSummary" <asp:ValidationSummary ID="FormSummary" runat="server" CssClass="errorMessage" />

  string InputValidationText = utilities.InputValidation(driver, inputs, i, nextButton, ErrorMessageID)
  if(InputValidationText != string.Empty)
  {
    //LogError
  }
}

【讨论】:

    【解决方案3】:

    我总是喜欢将事情分解成相关信息的组。例如,如果我有一个用户类,我可能会将其分解为几个较小的类:LoginCredentials、ProfileInfo、Settings 等,但我通常仍然会有一个包含这些子类的顶级 User 类。

    我肯定会推荐的一件事是将对象传递给一个 FillForm 函数,而不是所有这些单独的函数。使用这种方法有一些很大的优势。第一,您可以拥有一些用于许多测试用例的“通用”预配置对象。例如:

    public class FormInfo
    {
       string Domain;
       string Name;
       string Category;
       // etc...
    
      public FormInfo(string domain, string name, string category)
      {
         Domain = domain;
         Name = name;
         Category = category;
         // etc...
      }
    }
    
    
    // Somewhere in your initialization code
    public static FormInfo Info1 = new FormInfo("myDomain1", "myName1", "myCategory1");
    public static FormInfo Info2 = new FormInfo("myDomain2", "myName2", "myCategory2");
    
    You can still update one of your common merchants if you need to do something one-off:
    
    // In your test case:
    Info1.Category = "blah";
    FormPage.FillForm(Info1);
    

    或者,如果需要,您可以为特定的测试用例创建一个全新的商家对象。您还可以使用这些对象来执行字段验证之类的操作,或者我通常会破坏页面对象模式以进行特定字段验证,因此如果我正在验证商家域字段,我可能会这样做:

    Info1.Domain = null; //This should make the FillForm function skip doing anything with this field.
    FormPage.FillForm(Info1);
    FormPage.DomainTextBox.Text = "field validation string";
    

    这种方法的另一个重要优点是,如果页面被更新以添加、删除或修改字段,您只需要更新您的 FormInfo 对象和 FillForm 函数,并且不需要修改调用FillForm 函数 - 假设他们正在使用您常用的 FormInfo 对象之一。获得更多覆盖的另一种可能性是设置一个常见的 FormInfo 对象,以便为每个符合最小/最大长度的字段生成随机字符串,并在所有不同的允许字符之间循环。这允许您从同一组测试中获得一些额外的测试,但如果您开始仅从特定字符串获取失败结果,它也会增加一些噪音,所以要小心。

    【讨论】:

      【解决方案4】:

      除了您的enterWhatever() 方法之外,我通常还创建一个createWhatever(field1, field2, ...) 方法,我可以将其用作创建任何表单构建的快速路径,以便在真正用途时使用测试是另一回事。因此,如果我需要创建客户来测试提交工单,测试会转到CreateACustomer 页面并调用createCustomer(firstName, lastName, emailAddress, ...),然后继续执行更细致的任务,即使用该客户创建工单.

      【讨论】:

        【解决方案5】:

        【讨论】:

          【解决方案6】:

          为了读者的利益,我正在回答一个老问题。

          除了这里的其他好答案,我想在这里为那些刚接触 POM 的人添加一些建议。

          页面对象是一种众所周知的设计模式,被自动化工程师广泛接受,它为应用程序的每个页面创建单独的类文件,以将所有元素分组为属性,并将它们的行为/业务功能分组为类的方法。但它在为页面创建类时几乎没有问题——尤其是当页面有更多/不同的元素集/复杂元素(如网格/日历小部件/HTML 表格等)时。

          该类可能包含太多需要处理的职责。它应该被重组并分解成更小的类。即,跟随Single Responsibility Responsible

          在此处查看图片以了解想法。

          也就是说,创建可重用的页面片段并让主页对象为页面片段提供服务。

          查看here了解更多信息。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-08-09
            • 2022-11-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-16
            • 1970-01-01
            相关资源
            最近更新 更多