尚学堂 老师好!

上海:15201841284

广州:020-2989 6995

深圳:0755-23061965

武汉:027-8798 9193

西安:029-8822 8155

来自Serenity的关于Java 8的一些使用技巧

  Java 8早在2014年就出来了,但是我仍发现有很多团队并没有尽量多的去使用其新的特性。可以说Java 8中最大的新特性就是Lambda表达式,这终于给Java世界引入了函数式编程的味道。

  更多精彩内容以及学习资料,尚学堂论坛bbs.bjsxt.com免费下载。

  在这篇文章中,我想要给大家看一个简单的例子,通过这个具体的例子来展示Java 8和Lambda表达式是如何使你的生活更轻松的。 假如我们想要为一个使用frame或iframe的遗留应用编写一个测试。 Java的API 就是一个很好的例子。使用WebDriver对使用frame和iframe的页面进行自动化测试会比较很棘手,因为当你要操作一个网页元素时,你需要首先切换到它所在的frame中,就像这样:

  driver.switchTo().frame("MyFrame");

  button.click();

  driver.switchTo().defaultContent();

  让我们来看看在Java 8里如何做会更优雅些。设想我们需要写一个测试来检查什么时候从Packages列表里选择一个包,该列表即在frame的左下方出现的正确的类和接口的列表。我们可以写一个相当简单的Serenity BDD(译者注:一个自动化验收测试报告的类库 ,之前的名字是Thucydides)测试,如下所示:

  @RunWith(SerenityRunner.class)

  public class WhenConsultingPackageDetails {

  @Managed WebDriver driver;

  JavaAPIDocs apiDocs;

  @Test

  public void should_be_able_to_view_the_classes_for_a_given_package() {

  apiDocs.open();

  apiDocs.selectAPackage("java.applet");

  assertThat(apiDocs.getClassesAndInterfaces())

  .contains("AppletContext",

  "AppletStub",

  "AudioClip",

  "Applet");

  }

  }

  JavaAPIDocs类是一个Serenity Page Object,这个Serenity将会为我们初始化。它的代码如下所示:

  @DefaultUrl("https://docs.oracle.com/javase/7/docs/api/")

  public class JavaAPIDocs extends PageObject {

  @FindBy(tagName = "li")

  private List packageNames;

  public JavaAPIDocs(WebDriver driver) {

  super(driver);

  }

  public void selectAPackage(final String packageName) {

  ...

  }

  public List getClassesAndInterfaces() {

  ...

  }

  }

  现在假设我们想要实现selectAPackage方法。使用Java 7时,我们需要首先定位至该frame,找到将要点击的对应链接,然后返回主窗口。

  public void selectAPackage(final String packageName) {

  getDriver().switchTo().frame(("packageListFrame")

  find(By.linkText(packageName)).click();

  getDriver().switchTo().defaultContent();

  }

  而在Java 8里,我们则可以创建一个类,用于在frame之间切换,然后将要执行的操作(以Lambda表达式)传入。 假设我们将该类命名为InFrame。 我们的方法看起来是这样的:

  private InFrame inAFrame;

  public JavaAPIDocs(WebDriver driver) {

  super(driver);

  inAFrame = new InFrame(driver);

  }

  public void selectAPackage(final String packageName) {

  inAFrame.called("packageListFrame")

  .attemptTo(() -> find(By.linkText(packageName)).click());

  }

  注意到现在的selectAPackage()方法变得有多易读了吗?奇迹就发生在InFrame类里,它包含了一个attemptTo方法,其任务是要执行以参数形式传给它的Lambda表达式:

  public void attemptTo(UIPerformable performable) {

  driver.switchTo().frame(iframeNameOrId);

  performable.perform();

  driver.switchTo().defaultContent();

  }

  UIPerformable 是一件简单的函数式接口,我们可以将一个Lambda表达式传递给它:

  @FunctionalInterface

  public interface UIPerformable {

  void perform();

  }

  现在我们可以使用该方法去执行一段WebDriver代码,可随我们的需要可简可繁。我们就能保证在操作开始之前能够切换到正确的frame,并且在完成后再切换回来。

  getClassesAndInterfaces() 方法则看起来是相似的:

  public List getClassesAndInterfaces() {

  return inAFrame.called("packageFrame").retrieve(() ->

  packageNames.stream()

  .map(WebElement::getText)

  .collect(Collectors.toList())

  );

  }

  在这里,我们使用retrieve() 方法,该方法接受了一个Supplier, 这是标准的Java 8函数式接口之一。跟之前的例子一样,我们切换到该frame,执行操作,然后又切换回去。只是这一次该操作返回了一个值:

  public T retrieve(Supplier performable) {

  driver.switchTo().frame(iframeNameOrId);

  T result = performable.get();

  driver.switchTo().defaultContent();

  return result;

  }

  我们也可以使用Java 8的streams功能将由WebDriver返回的web元素的列表转换为字符串列表,这是Java 8的另外一个不错的特性。

  完整的InFrame 类如下所示:

  public class InFrame {

  private final WebDriver driver;

  public InFrame(WebDriver driver) {

  this.driver = driver;

  }

  public InstantiatedIFrameContainer called(String iframeNameOrId) {

  return new InstantiatedIFrameContainer(driver, iframeNameOrId);

  }

  public class InstantiatedIFrameContainer {

  private final WebDriver driver;

  private final String iframeNameOrId;

  public InstantiatedIFrameContainer(WebDriver driver,

  String iframeNameOrId) {

  this.driver = driver;

  this.iframeNameOrId = iframeNameOrId;

  }

  public void attemptTo(UIPerformable performable) {

  driver.switchTo().frame(iframeNameOrId);

  performable.perform();

  driver.switchTo().defaultContent();

  }

  public T retrieve(Supplier performable) {

  driver.switchTo().frame(iframeNameOrId);

  T result = performable.get();

  driver.switchTo().defaultContent();

  return result;

  }

  }

  }

  这个类最初需要多花费一点精力去编写,但是在一个拥有很多frame的应用里,这点努力很快就会显得很值,因为换来了更易读和简介的页面对象(Page Object)和测试代码。

  更多精彩内容以及学习资料,尚学堂论坛bbs.bjsxt.com免费下载。

  • 北京校区
  • 西安校区
  • 山西校区
  • 武汉校区
  • 深圳校区
  • 上海校区
  • 广州校区
  • 保定招生办
  • 黑龙江项目办

北京京南校区:北京亦庄经济开发区科创十四街6号院1号楼 赛蒂国际工业园
咨询电话:400-009-1906 / 010-56233821
面授课程: JavaEE+微服务+大数据     大数据+机器学习+平台架构     Python+数据分析+机器学习  人工智能+模式识别+强化学习   WEB前端+移动端+服务端渲染

地址:陕西省西安市高新区西安软件园西区创新信息大厦A座三层尚学堂

电话:029-88228155 / 18291433445

山西学区地址:山西省晋中市榆次区大学城大学生活广场万科商业A1座702

武汉学区地址:武汉市东湖高新区光谷金融港B22栋11楼
咨询电话:027-87989193

深圳校区地址:深圳市宝安区航城大道U8智造产业园U6栋3楼
咨询电话:0755-23061965 / 18898413781

上海尚学堂校区地址:上海市浦东新区城丰路650号
咨询电话:021-67690939

广州校区地址:广州市天河区车陂街道大岗路5号中侨广场2栋321室(四号线车陂站D出口,或brt车陂站)
咨询电话:18948349646

保定招生办公室

地址:河北省保定市竞秀区朝阳南大街777号鸿悦国际1101室

电话:15132423123

黑龙江项目办
地点:哈尔滨市松北区博文路青年部落孵化器1层
电话:15321415678
Copyright 2006-2021 北京尚学堂科技有限公司  京ICP备13018289号-19  京公网安备11010802015183   营业执照
网站维护:北京尚学堂科技有限公司昌平分公司