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

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

在这篇文章中,我想要给大家看一个简单的例子,通过这个具体的例子来展示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() {

        // Given
        apiDocs.open();

        // When
        apiDocs.selectAPackage("java.applet ");

        // Then
        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<WebElementFacade> packageNames;

    public JavaAPIDocs(WebDriver driver) {
        super(driver);
    }

    public void selectAPackage(final String packageName) {
        ...
    }

    public List<String> 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<String> getClassesAndInterfaces() {
    return inAFrame.called("packageFrame ").retrieve(() ->
            packageNames.stream()
                    .map(WebElement::getText)
                    .collect(Collectors.toList())
    );
}

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

public <T> T retrieve(Supplier<T> 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> T retrieve(Supplier<T> performable) {
            driver.switchTo().frame(iframeNameOrId);
            T result = performable.get();
            driver.switchTo().defaultContent();
            return result;
        }
    }
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值