Enterprise Java

One Jar To Rule Them All: Arquillian + Java 8

arquillian_ui_success_256pxWith Java 8, a lot of new language improvements have been implemented for making life of developer easier. In my opinion, one of the greatest things it has Java 8 is that in some situations developed code looks more beautiful that using prior approaches, and I am referring in Lambdas and Method references. This post is not about learning these Java 8 features but how to apply them in Arquillian framework.

I have detected four use cases where method references and lambdas can be used in Arquillian. Here you can see them, and of course if you found any other one, feel free to share with us.

Merge libs inside a JavaArchive

To write tests with Arquillian you need to create the deployment file programmatically (jar, war or ear). This is accomplished using Shrinkwrap. Your deployment file sometimes will require you add some external dependencies on it. Typical example is when you are creating a WebArchive and you need to add some dependencies to WEB-INF/lib. In this case it is easy because there WebArchive class has a method called addAsLibraries which basically adds the given jars in the libraries path.

But what’s happening when your deployment file is a jar file? Then you need to merge each library inside JavaArchive object by using merge method.

private static JavaArchive mergeHibernate(JavaArchive javaArchive) {
    JavaArchive[] javaArchives = Maven.resolver().resolve("org.hibernate:hibernate-entitymanager:4.2.15.Final").withTransitivity().as(JavaArchive.class);
    for(JavaArchive hibernateDep: javaArchives) {
        javaArchive.merge(hibernateDep);
    }
    return javaArchive;
}

This is a way to do it but with Java 8, you can use foreach function and method references.

private static JavaArchive mergeHibernate(JavaArchive javaArchive) {
    JavaArchive[] javaArchives = Maven.resolver().resolve("org.hibernate:hibernate-entitymanager:4.2.15.Final").withTransitivity().as(JavaArchive.class);
    Arrays.stream(javaArchives).forEach(javaArchive::merge);
    
    return javaArchive;
}

Note that we are converting the Array into a stream so we can call foreach function. In version 2.2.0 of ShrinkWrap Resolver you will be able to get dependencies as List, so you will be able to get an stream without any conversion. Next important point is that we are using method reference feature to merge all dependencies. Now with a single line we can merge all dependencies.

 Creating custom Assets

Arquillian uses ShrinkWrap to create the deployment file and add resources inside. These resources are added by using any of the methods provided by the API like add, addClass, addAsManifestReource and so on. These methods can receive as first parameter an Asset. Asset is an interface which contains only one method called openStream which returns an InputStream. Assets are used for setting the content of file that will be be added inside the deployment file.

For example:

archive.add(new StringAsset("My txt file"), "hello.txt");

ShrinkWrap comes with some already defined assets like Url, String, Byte, Class, … but sometimes you may need to implement your own Asset.

ShrinkWrap.create(JavaArchive.class).addAsManifestResource( 
                                          new Asset() {
                                             public InputStream openStream() {
                                                  return new CheckedInputStream(urlInputStream, crc32)
                                             }
                                          }, 
                                     "persistence.xml");

In this case we are using an inner-class, but because Asset class can be considered a functional interface (only one abstract method), we can use Lambdas to avoid the inner class.

So much simple and more readable.

Parsing HTML tables

If you are using Arquillian Drone or Arquillian Graphene, you will be use some WebDriver Selenium classes for getting web page elements. Sometimes you need to validate columns of and HTML table, and in this cases you can end up by having a lot of boilerplate code iterating over columns and rows to validate that contains the correct values.

Your code prior to Java 8 will look something like:

List<WebElement> elements = session.findElements(xpath("//table/tbody/tr/td/span[@class='title']"));

List<String> columnValues = new ArrayList<String>();
for(WebElement webElement : elements) {
  columnValues.add(webElement.getText());
}

return columnValues;

But in Java 8, with the addition of streaming API, the code becomes much easier and readable:

List<WebElement> elements = session.findElements(xpath("//table/tbody/tr/td/span[@class='title']"));
return elements.stream().map(WebElement::getText).collect(Collectors.toList());

As you can see code is pretty much compact. What we are doing here is first of all getting all web elements of column title, no news here. But then streaming API comes to play. First we create an stream from a list by calling stream method. Then what we are doing is calling the method getText from all WebElements present in the list. And finally the list of strings which in fact is a list of content of all rows of column title is returned.

See that in this case the code is much readable than previous one, and more important is that you can even create a parallel stream to get all power of multi-core processors.

So as you can see Java 8 can be used not only in business code but also in tests.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button