我需要重构我的Selenium测试,并查看其LoadableComponent Wiki页面。我开始尝试使用它,尽管我无法真正理解它的好处。我看了看他们的当前实施情况(截至2011-07-26),现在更加困惑了。该代码基本上是使用错误处理来控制执行流程。我想到的东西很奇怪:

 @SuppressWarnings("unchecked")
public T get() {
  try {
    isLoaded();
    return (T) this;
  } catch (Error e) {
    load();
  }

  isLoaded();

  return (T) this;
}
 


即违反了设计原则“ “仅将异常用于特殊用途”,这里,异常是规则,并且isLoaded至少在第一次调用该方法时,预计会引发异常。

此外,此LoadableComponent是抽象类,这意味着我不能拥有从父组件继承的子组件。

这一切对我来说都是错误的。我只是为每个页面对象实现一个get()方法,该方法将根据需要根据Selenium驱动程序的状态加载页面,而无需捕获异常。我把所有这些弄错了吗?

#1 楼

当您问“我把所有这些弄错了吗?”时,我不知道您指的是哪个“这个”。根据Wiki页面,isLoaded方法应使用JUnit断言来检查是否实际加载了页面。 JUnit断言抛出错误而不是异常,以表示出现问题。

LoadableComponent可以将isLoaded重构为两种方法:一种(称为isLoadedBoolean)返回指示页面是否已加载的布尔值,另一种(称为assertLoaded)使用JUnit断言。当然,如果检查页面是否已加载需要评估多个条件,则assertLoaded指示哪个条件失败可能很有价值。懒惰的人可能会这样做:

protected void assertLoaded() {
  assertTrue(condition1,"condition 1 failed");
  assertTrue(condition2,"condition 2 failed");
}

protected boolean isLoadedBoolean() {
  try {
    assertLoaded();
    return true;
  }
  catch (Error e) {
    return false;
  }


其他人会认为设计太怪异了,而是这样做:

protected void assertLoaded() {
  assertTrue(condition1,"condition 1 failed");
  assertTrue(condition2,"condition 2 failed");
}

protected boolean isLoadedBoolean() {
  return condition1 && condition2;
}


当然,如果检查页面是否加载的标准发生了变化(当然,因为这是对Web界面的测试,所以它也会发生变化),这两种方法都需要更新。因此,以这种方式编写代码的开发人员将以怪异的方式换取维护错误。

我想另一种选择是这样的:

public class PageLoadCheckFailure {
   public String reason;
   ... other interesting data ...

   public PageLoadCheckFailure(reason, ... other interesting data ...) {
      this.reason = reason;
      etc.
   }

   public String getReason() {
      return reason;
   }

   ... getters for other interesting data ...
}

protected PageLoadCheckFailure checkWhetherLoaded() {
   if (!condition1) {
      return new PageLoadCheckFailure("condition 1 failed", ... other interesting data...);
   }
   if (!condition2) {
         return new PageLoadCheckFailure("condition 1 failed", ... other interesting data...);
   }
   return nil;
}

protected void assertLoaded() {
  PageLoadCheckFailure result = checkWhetherLoaded();
  if (result != nil) {
     Assert.fail(result.toString());
  }
}

protected boolean isLoadedBoolean() {
  return checkWhetherLoaded() == nil;
}


但是与Selenium设计相比,这肯定是很多工作,特别是因为毕竟这只是测试代码。

评论


非常感谢您的回答。恐怕我没有对此考虑足够长的时间,而您的推理正是我所缺少的。我不了解的两件事是:1-为什么使用错误来控制流程? (我写了“ exceptions”,意思是必须捕获的Throwables);和2-为什么LoadableComponent是必须扩展的类,而不是接口?

–阿尔贝托
2011年11月28日在13:31

您已经回答了我的第一个问题,因为在阅读您的回答后,我注意到我从他们的文档中遗漏的重要部分:“通过使用这些断言,可以为类的用户提供清晰的信息,这些信息可以用于调试测试。”就是说,与提供大量信息的assertTrues列表相比,您的checkWheterLoaded方法过于繁琐。我应该已经阅读了stackoverflow中的“真的是异常错误的例外吗”问题!

–阿尔贝托
2011年11月28日在13:31

我还是不太喜欢第二期。我正在使用PageObjects层次结构来建模UI的嵌套组件。以他们的例子为例。对我来说,EditIssue组件还在其API中提供了signOut()方法是有意义的(它与SecuredPage组件有关)。但是,LoadableComponent实现非常简单,可以直接包含在我的根组件中,因此现在也适合我。

–阿尔贝托
2011-11-28 13:31