Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Serenity Tricks With Java 8

DZone's Guide to

Serenity Tricks With Java 8

Lambda expressions can be used to help set up and pass your testing code. This guide shows you how Java 8's functional aspects can synergize with Serenity.

· Java Zone
Free Resource

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

Java 8 came out back in 2014, but I still find many teams not making as much use of its features as they could. Arguably the biggest new feature in Java 8 were Lambda expressions, which finally brought a taste of functional programming to the Java world.

In this article, I want to give a simple example of how Java 8 and Lambda expressions can make your life easier, through a concrete example. Suppose we want to write a test for one of the few remaining applications that use frames or iframes. The Java APIs are a good example. Automating pages with frames and iframes using WebDriver is tricky because, when you manipulate a web element, you first need to switch to the frame it is in, like this:

driver.switchTo().frame("MyFrame");
button.click();
driver.switchTo().defaultContent();


Let's see how we could make this nicer in Java 8. Imagine we need to write a test to check that when you select a package in the Packages list, the correct list of classes and interfaces appears in the frame to the lower left of the screen. We could write a very simple Serenity BDD test like this:

@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");
    }
}


The JavaAPIDocs is a Serenity Page Object, that Serenity will initialise for us. It looks something like this.

@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() {
        ...
    }
}


Now suppose we want to implement the selectAPackage method. Using Java 7, we would need to first navigate to the frame, find the corresponding link to click on, then navigate back to the main window:

public void selectAPackage(final String packageName) {
    getDriver().switchTo().frame(("packageListFrame")
    find(By.linkText(packageName)).click();
    getDriver().switchTo().defaultContent();
}


In Java 8, on the other hand, we could create a class whose responsibility it is to switch to and from frames, and then just pass in the operation (as a Lambda expression) that we want to perform. Suppose we call this class InFrame. Our method would become something like this:

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());
}


Notice how much more readable the selectAPackage() method just became? The magic happens in the InFrame class, which contains a method, attemptTo, whose job is to execute a lambda expression passed to it as a parameter:

public void attemptTo(UIPerformable performable) {
    driver.switchTo().frame(iframeNameOrId);
    performable.perform();
    driver.switchTo().defaultContent();
}


The UIPerformable is a simple functional interface that we can use to pass a lambda expression to the method:

@FunctionalInterface
public interface UIPerformable {
    void perform();
}


We can now use this method to execute a block of WebDriver code, as simple or as complex as we need. We can be sure that we will switch to the right frame before the operation starts, and switch back when we are done.

The getClassesAndInterfaces() method is similar:

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


Here, we use the retrieve() method, which takes a Supplier, one of the standard Java 8 functional interfaces. Like in the previous example, we switch to the frame, perform the operation, then switch back. Only this time, the operation returns a value:

public <T> T retrieve(Supplier<T> performable) {
    driver.switchTo().frame(iframeNameOrId);
    T result = performable.get();
    driver.switchTo().defaultContent();
    return result;
}


We also use Java 8 streams to convert the list of web elements returned by WebDriver to a list of Strings, another nice feature of Java 8.

The full InFrame class is shown here:

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;
        }
    }
}


This class took a little more effort to write initially, but in an application with a lot of frames, the effort would pay itself off very quickly through more readable and more concise Page Object and test code.

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:
java 8 ,java 8 functional programming ,serenity bdd ,lambda expressions ,tutorial

Published at DZone with permission of John Ferguson Smart, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}