【问题标题】:Can I organize objects in page object model with arrays or hash maps?我可以用数组或哈希映射组织页面对象模型中的对象吗?
【发布时间】:2018-04-22 02:18:23
【问题描述】:

我是 Selenium 自动化的新手。而且我对java有相当的了解。

我创建了用于用户注册的测试脚本。

我为此使用了页面对象模型。这是我的页面对象脚本。

这就是我用的

public class SIgnUpTest extends PageObject {

@FindBy(id="merchantName")
private WebElement merchant;

@FindBy(id="merchantCode")
private WebElement code;

@FindBy(id="categoryId")
private WebElement category;

@FindBy(id="description")
private WebElement description;

@FindBy(id="merchantLogo")
private WebElement logo;

@FindBy(id="btnNextStep1")
private WebElement Next;

public SIgnUpTest(WebDriver driver) {
    super(driver);
}
public void enterName(String name, String code,String description){
    this.merchant.sendKeys(name);
    this.code.sendKeys(code);
    this.description.sendKeys(description); 
}
public void Logo(String Logo){
    this.logo.sendKeys(Logo);       
}
public void Next() {
    Next.click();
}

我有大约 100 个对象要像这样添加。 我该如何组织它以供长期使用?

除了重复@Find by 和 WebElement 之外,有没有其他方法可以使用?

我一直在寻找数组和哈希映射。但我不知道如何使用它。

我可以为此使用二维数组或哈希映射吗?如果有怎么办?

谢谢。

我想使用这样的东西。 可能吗?

有什么方法可以使用类似于我所寻求的以下内容:

以下代码有问题。

public class SIgnUpTest extends PageObject {
public void objects (){

SortedMap sm = new TreeMap();
sm.put("merchantName", "merchant");
sm.put("merchantCode", "code");
sm.put("categoryId", "category");
sm.put("description", "description");
sm.put("merchantLogo", "Logo");
sm.put("Next", "Next")

for(int i=0; i<sm.keySet().size(); i++){
@FindBy(id=sm.keySet());
}
for(int i=0; i<sm.keySet().size(); i++){
    private WebElement sm.values(); 
}   
}   
public SIgnUpTest(WebDriver driver) {
    super(driver);
}
public void enterName(String name, String code,String description){
    this.merchant.sendKeys(name);
    this.code.sendKeys(code);
    this.description.sendKeys(description); 
}
public void Logo(String Logo){
    this.logo.sendKeys(Logo);       
}   
public void Next() {
    Next.click();
}

}

【问题讨论】:

    标签: java arrays selenium junit pageobjects


    【解决方案1】:

    是的,有多种方法可以解决此问题。 我猜你有一个很大的页面,里面有很多你需要与之交互的元素。不使用 pagefactory 方法的一种方法(通常与 PageObject 模型混淆)。

    您可以使用驱动程序实例在需要时使用 driver.findElement(By.xpath("//xpath/to/element"));

    或者,您可以使用内部类在逻辑上封装页面上的不同模块。 我建议您首先通过以下链接更清楚地了解设计决策:

    https://sqa.stackexchange.com/questions/14889/how-do-i-split-my-pageobject-model-classes https://martinfowler.com/bliki/PageObject.html

    【讨论】:

    • 我使用了 driver.findElement .. 但我考虑了对象的可重用性。这是否意味着我不应该使用这种模式?
    • 您可以使用 Page Class 私有 WebElement 对象,然后根据需要填充它们。但是,根据我的经验,重用 WebElements 会导致许多 StaleElementReference 异常。我没有存储 WebElements,而是将 By 定位器存储在 Page Class 中,当需要时在方法中加载它们,并且 WebElements 存储在方法的范围内。
    【解决方案2】:

    根据我的经验,我更喜欢 ObjectMap -over- PageObject。

    ObjectMap(有助于避免学习整个 PageObj 复杂抽象层和分类)可以在 ---Selenium 测试工具手册,第 2 版---bookseleniumeasy 中找到。将其视为Composition-over-inheritance,倾向于更灵活和强大的,避免设计复杂的分类法。还支持数据驱动的方法,选择器不是在您的代码中硬编码 == 不可能进行配置。 PageObj 将您的代码与复杂的抽象联系起来,因此为了重用它,您总是需要在您的网站中使用这个特定页面。配置文件有它们的位置,但是当它们与代码一起打包并且不打算由用户更新时,更新配置文件中的值所需的步骤与更新代码中的值一样多,所以有真的没有发生封装。这里动态表单(PageObj/BEM)是根据测试的需要在运行时构建的。不再有复杂的表格分类法。

    例子

    basic_contact > zip_code_contact > dealer (message+dealer info header) > quote (model+trim) > test_drive (calendar) > contact_us (email+chat)
    

    在立即开始使用 POM(如 holmium.corepage-objects)之前,请注意以下几点:

    1. 自动构建的页面对象难以维护和使用。几乎没有将元素分组到页眉和页脚或已识别的小部件中,只会有一大堆东西 - 自动生成的名称是否足以解释它们的用途?

    2. 它可能会限制您的设计,例如你盯着忽略更好的抽象。

    3. 灵活性不够,尤其是重构(结构和实现)。

    找到more at dzone。 Java herehere 的实现。

    【讨论】:

      【解决方案3】:

      您需要创建自定义注解、元素工厂和装饰器来完成此操作。在下面的代码中添加适当的导入。

      注释

      @Retention(RetentionPolicy.RUNTIME)
      @Target({ElementType.FIELD})
      public @interface MultipleFind {
      
          String delimiter() default "@@";
          String[] elementDetails();
      }
      

      Sample PageObject - usernameInput@@ID@@username - 第一部分将用作key在地图中。 second part定位器策略,这将对应于How.java enum 中的值。 第三部分定位器。此页面对象可以包含标准注释,如 FindBy 等以及自定义注释。

      public class LoginPageObject extends BasePageObject<LoginPageObject> {
      
          //Conventional declaration
          @FindBy(how=How.ID, using="username")
          private WebElement usernameInput;
      
          //Conventional declaration
          @FindBy(how=How.NAME, using="pwd")
          private WebElement passwordInput;
      
          //Custom multiple annotation
          @MultipleFind(elementDetails = { "usernameInput@@ID@@username", "passwordInput@@NAME@@pwd" })
          private Map<String, WebElement> loginui;        
      
          public LoginMapPageObject(WebDriver driver) {           
              super(driver);
      
              //PageFactory initialization with custom factory and decorator
              MapElementLocatorFactory melf = new MapElementLocatorFactory(driver);
              MapFieldDecorator mfd = new MapFieldDecorator(melf);
              PageFactory.initElements(mfd, this);
          }
      
          private void enterAndSubmitLoginDetails() {         
              //Conventional calls
              //usernameInput.sendKeys("user");
              //passwordInput.sendKeys("password");
      
              //Call to retrieve element from map
              loginui.get("usernameInput").sendKeys("user");
              loginui.get("passwordInput").sendKeys("password");
          }       
      }
      

      地图注解 -

      public class MapAnnotations extends Annotations {
          public MapAnnotations(Field field) {
              super(field);
          }
      
          public By buildBy() {
              if (getField().getAnnotation(MultipleFind.class) != null)
                  return null;
              return super.buildBy();
          }
      
          public Map<String, By> buildMapBys() {
              Map<String, By> details = null;
              Optional<Annotation> annot = Arrays.asList(getField().getAnnotations())
                      .stream()
                      .filter(a -> a.annotationType().equals(MultipleFind.class))
                      .findAny();
              if(annot.isPresent()) {
                  details = createLocatorDetails(annot.get());
              }
              return details;
          }
      
          private Map<String, By> createLocatorDetails(Annotation annot) {
              String[] elemDets = ((MultipleFind) annot).elementDetails();
              String delim = ((MultipleFind) annot).delimiter();
              Map<String, By> details = Arrays.stream(elemDets)
                      .map(d -> d.split(delim))
                      .collect(Collectors.toMap(a -> a[0], a -> createBy(a[1], a[2])));
              return details;
          }
      
          private By createBy(String howStr, String using) {
              How how = How.valueOf(howStr);
              switch (how) {
                case CLASS_NAME:
                  return By.className(using);
                case CSS:
                  return By.cssSelector(using);
                case ID:
                case UNSET:
                  return By.id(using);
                case ID_OR_NAME:
                  return new ByIdOrName(using);
                case LINK_TEXT:
                  return By.linkText(using);
                case NAME:
                  return By.name(using);
                case PARTIAL_LINK_TEXT:
                  return By.partialLinkText(using);
                case TAG_NAME:
                  return By.tagName(using);
                case XPATH:
                  return By.xpath(using);
                default:
                  // Note that this shouldn't happen (eg, the above matches all
                  // possible values for the How enum)
                  throw new IllegalArgumentException("Cannot determine how to locate element ");
              }
          }
      
          protected void assertValidAnnotations() {
              FindBys findBys = getField().getAnnotation(FindBys.class);
              FindAll findAll = getField().getAnnotation(FindAll.class);
              FindBy findBy = getField().getAnnotation(FindBy.class);
              MultipleFind multFind = getField().getAnnotation(MultipleFind.class);
              if (multFind != null
                      && (findBys != null || findAll != null || findBy != null)) {
                  throw new IllegalArgumentException(
                          "If you use a '@MultipleFind' annotation, "
                                  + "you must not also use a '@FindBy' or '@FindBys' or '@FindAll' annotation");
              }
              super.assertValidAnnotations();
          }
      }
      

      MapElementLocatorFactory

      public class MapElementLocatorFactory implements ElementLocatorFactory {
      
          private final SearchContext searchContext;
      
            public MapElementLocatorFactory(SearchContext searchContext) {
              this.searchContext = searchContext;
            }
      
            public MapElementLocator createLocator(Field field) {
              return new MapElementLocator(searchContext, field);
            }
      }
      

      地图元素定位器

      public class MapElementLocator extends DefaultElementLocator {
      
          private Map<String, By> elementBys;
          private SearchContext searchContext;
      
          public MapElementLocator(SearchContext searchContext, Field field) {
              this(searchContext, new MapAnnotations(field));
          }
      
          public MapElementLocator(SearchContext searchContext,MapAnnotations annotations) {
              super(searchContext, annotations);
              this.elementBys = annotations.buildMapBys();
              this.searchContext = searchContext;
          }
      
          public WebElement findElement(String elementName) {
              By by = elementBys.get(elementName);
              return searchContext.findElement(by);
          }
      }
      

      MapFieldDecorator

      public class MapFieldDecorator extends DefaultFieldDecorator {
      
          public MapFieldDecorator(ElementLocatorFactory factory) {
              super(factory);
          }
      
          public Object decorate(ClassLoader loader, Field field) {
              if (Map.class.isAssignableFrom(field.getType())) {          
                  MapElementLocator locator = (MapElementLocator) factory.createLocator(field);           
                  return proxyForMapLocator(loader, locator);
              }
              return super.decorate(loader, field);
          }
      
          @SuppressWarnings("unchecked")
          protected Map<String, WebElement> proxyForMapLocator(ClassLoader loader,
                  MapElementLocator locator) {
              InvocationHandler handler = new LocatingMapElementHandler(locator);
              Map<String, WebElement> proxy;
              proxy = (Map<String, WebElement>) Proxy.newProxyInstance(loader,
                      new Class[] { Map.class }, handler);
              return proxy;
          }
      }
      

      LocatingMapElementHandler

      public class LocatingMapElementHandler implements InvocationHandler {
          private final MapElementLocator locator;
      
          public LocatingMapElementHandler(MapElementLocator locator) {
              this.locator = locator;
          }
      
          public Object invoke(Object object, Method method, Object[] objects) throws Throwable {
              if(method.getName() != "get")
                  throw new UnsupportedOperationException("Only get method of Map is supported for this proxy.");     
              return locator.findElement((String)objects[0]);
          }
      }
      

      这仅适用于单个 WebElement 而不是列表。您可以尝试实现它,但问题是擦除会删除通用信息,因此无法正确确定您是否需要单个或多个元素。只有 get() 方法可以在 Map 代理上工作,其他的会抛出 UnSupportedException。

      【讨论】:

        猜你喜欢
        • 2018-12-04
        • 2010-11-26
        • 1970-01-01
        • 2017-08-23
        • 2020-12-13
        • 2022-06-17
        • 2014-12-03
        • 2015-01-06
        • 1970-01-01
        相关资源
        最近更新 更多