Thursday, May 31, 2018

Selenium vs FluentLenium


1) Introduction
2) Selenium
3) FluentLenium
4) Conclusion

1) Introduction

All my previous posts labeled with Selenium tag featured FluentLenium syntax. Why? Even though I tried to answer it in FluentLenium focused posts (Introduction & Waiting game) I never really did full comparison against Selenium. As you probably already figured out reading the title this post will show how FluentLenium can help you write better, shorter and easier to maintain Selenium tests.

Just to be clear that we are on the same page: FluentLenium is only Selenium extension, not alternative. It's important to keep in mind that it's still GUI automation with all it's flaws and difficulties. FluentLenium was designed to mitigate these problems as shall be shown here.

Warning: this post is code rich.

2) Selenium

I'm going to start with pure Selenium first. Let's say we want to automate the following scenario:

a) Search for 'public speaking' term on my blog. Make sure the search results page contains some posts.
b) Open first post and verify that it contains comment section.

I chose to verify comment section, because it's loaded dynamically by Blogger. As you shall see pure Selenium struggles a bit with such elements and fancy waiting is required for test stability.

Our obvious first step is adding Selenium dependency into pom.xml. I'm using 3.9.1 version here:

We need some properties next. I'm writing those words on Windows so I have to define driver paths. Let's implement the simplest possible class that handles it:

And next we need file in test resources folder:

So far so good. We are ready to start defining tests. Let's create a top level test class responsible for driver handling first. It will be our baseline. Normally I don't include imports in my snippets, but this Apache Commons entry is necessary for easy understanding of if in setUp() section. I have Mac with chrome driver system property already set by Brew and this line ensures that it will work on it without any changes.  I'm using JUnit btw.

Now we need baseline for page objects. I don't want to load elements from @FindBy annotation every single time, so I'm coding it on top level class.

Framework is ready so we can implement our page objects. Main blog page is obvious place to begin. I'm adding two methods, one to make sure we are indeed on correct page and the second one to use search functionality.

Next in flow is search results page. This time I'm adding two assertions (are we on correct page? are posts displayed?) and method which clicks on first post title.

And finally we are on post page. Comment section is not only in an iframe, but it also loads dynamically. In order to handle it I had to implement fancy wait, which polls every 500 milliseconds maximum 10 times. Element is not there at first, but we ignore NoSuchElementException and poll again.

Having all pieces implemented we can write a pure Selenium test.

3) FluentLenium

Now we will write the same test using FluentLenium. Spoiler alert: it will be easier :)

As usual we start with dependencies in pom.xml.

Driver handling is already taken care of by FluentLenium (FluentTest class). I want my tests to work both on Windows and Mac, so I'm adding a little extension. FluentLenium also allows to store driver names in human friendly way so I have added driver=chrome property. MyProperties class can be found above.

In Selenium section I have implemented PageObject class for framework preparations. It isn't required in FluentLenium. It's already handled by FluentPage class.

We can proceed to Page Object definition now. Let's start with Main Page. @PageUrl annotation is used to tell driver where to go. newInstance() method creates new page object class and isAt() is used for 'am I on correct page?' verification.

Now search result page with similar methods. Please take a look into @FindBy support for lists. Such feature isn't implemented in pure Selenium. It really helps to write easy to maintain code.

Finally, post page. You can see how useful await() methods are. Instead of defining separate Wait object we can use intuitive syntax. Also NoSuchElementExceptions are ignored by default. We have also very handy switchTo() method which changes context into iframe.

Having all pieces ready let's take a look at our final creation - a FluentLenium test. @Page is pseudo injection - it allow us to predefine pages used in test.

4) Conclusion

I'm engineer and I'm not a fan of arguments about which approach is better. I can only suggest you try FluentLenium and see how it works for you. Personally I'm a huge fan and I hope this post highlighted it's features. Doesn't it?

As usual all code can be downloaded from by GitHub page.