Selenide vs Fluentlenium #1 - Creating simple scenario
Selenium WebDriver is the most popular tool used for test automation of web applications. There are also a lot of frameworks which is based on Selenium but adds to it some useful functionality or solves some problems. This article is the first part of the series in which I compare two amazing Selenium based frameworks dedicated for Java language: Fluentlenium and Selenide. So if you are before making decision if the framework you will create should be based on Selenium WebDriver or any of its wrapper then this article is perfect for you. In this article we will focus on creating simple scenario in those two frameworks and describing the differences on that level.
Let's start from creating the simple test in Selenide. Assume that I just want to make the framework working and I am not focusing on the framework architecture. If I just want to start, Selenide looks really great because to make it working we just need to add Selenide requirement to maven and that is all.
<dependency> <groupId>com.codeborne</groupId> <artifactId>selenide</artifactId> <version>${selenide.version}</version> <scope>test</scope> </dependency>
We do not need to worry about drivers for all browsers we want to support and adding
them to system variables. The only thing we need to worry about is the browsers which
have to be installed. This works great at least with Firefox and Chrome (with IE you
still need perform some configuration). The one disadvantage I can think about is the
static configuration so I guess that we can't e.g. run tests in parallel in different
browsers.
When our maven dependencies are ready we can write our first test which looks like this:
package com.testcraftsmanship.selenide; import com.codeborne.selenide.Configuration; import org.junit.Test; import static com.codeborne.selenide.Condition.text; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.open; public class QuickStartTest { @BeforeClass public static void setUp() { Configuration.browser = "chrome"; } @Test public void itShouldBePossibleToSearchThePhrase() { open("https://duckduckgo.com"); $("#search_form_input_homepage").setValue("Selenium"); $("#search_button_homepage").click(); $(".module__title__link").shouldHave(text("Selenium")); } }
What the test do is just navigating to https://duckduckgo.com, searching the Selenium
phrase and verifying that this phrase is displayed in the title of the search results.
If we are talking about the code then the setUp() method is not required in the example
but I added it just to show you how can we define which browser we want to perform tests
on - here we select Google Chrome.
In the test I have used open("https://duckduckgo.com") method which simply open browser
and navigates to the https://duckduckgo.com web page. Method $("#search_form_input_homepage")
returns the Selenide wrapper of Web Element which is search by css selector passed as an
argument. The element allows us to perform many actions like setValue which enter the
text in the field, click() perform the click action. It is important to add that all
those actions contains the wait mechanism under the hood. It means that in most cases
you don't need to bother about timing issues (e.g. StaleElementException). Of course
in real world waits still have to be written.
If we want to achive the same simple case in Fluentlenium we have to add a few more dependencies to maven:
<dependency> <groupId>org.fluentlenium</groupId> <artifactId>fluentlenium-junit</artifactId> <version>${fluentlenium.version}</version> </dependency> <dependency> <groupId>org.fluentlenium</groupId> <artifactId>fluentlenium-assertj</artifactId> <version>${fluentlenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-chrome-driver</artifactId> <version>${selenium.version}</version> <scope>test</scope> </dependency>
Now we need to add separate dependency for test framework (e.g. for junit we have to
add fluentlenium-junit), if we want to have more advanced assertions then separate library
for assertions (e.g. fluentlenium-assertj as an extension for assertj) and we also have
to add selenium drivers dependencies for all browsers we have to test on. Unfortunately
we also have to download driver executable for all those browsers and add them to system
variables. It looks little bit more complicated but you have to do it only once. Because
you do it by your I think that you have a better control over all staff which happens
under the hood. The same test scenario in Fluentlenium looks like listed below:
package com.testcraftsmanship.fluentlenium; import org.fluentlenium.adapter.junit.FluentTest; import org.junit.BeforeClass; import org.junit.Test; import static org.fluentlenium.assertj.FluentLeniumAssertions.assertThat; public class QuickStartTest extends FluentTest { @BeforeClass public static void requiredSetUp() { String chromeDriverPath = QuickStartTest.class.getResource("/chromedriver.exe").getPath(); System.setProperty("webdriver.chrome.driver", chromeDriverPath); } @Test public void itShouldBePossibleToSearchThePhrase() { goTo("https://duckduckgo.com"); $("#search_form_input_homepage").fill().with("Selenium"); $("#search_button_homepage").submit(); assertThat($(".module__title__link")).hasText("Selenium"); } }
As we can see the test class is a little bit more complicated because we have to
extends FluentTest (different class has to be extended when we use TestNg as a test
runner). In the requiredSetUp() method I have added the part we have to somehow add.
We have to download and keep somewhere drivers for the browsers we want to test on
and set location to those browsers in desired system property (this part work the same
as with plane Selenium WebDriver). The syntax of methods used in the test looks very
similar to those from Selenide. Instead of open() method here goTo() method is used.
The method $("#search_form_input_homepage") also returns Fluendlenium wrapper of Selenium
Web Element. On the object we can perform a lot of dedicated methods which extends
functionality of Selenium Web Element. The main difference for now is that in Fluentlenium
we have a separate class with assertions which is some kind of wrapper of chosen assertion
library - in the example above it is wrapper extension of assertj so it has the same
syntax. It applies to me more than the solution from Selenide in which assertions can
be perform directly from the object you want to verify (of course it is matter of taste).
Unfortunately in Fluentlenium waits are not handled automatically which for most of people
will be a big disadvantage. In my personal opinion I prefer to control the way of waiting
for action to be performed - to avoid bugs covered by an unknown mechanism of waiting
for an action to be taken. Here I can see the risk that e.g. is it possible to miss
the bug in which to perform an action I have to click the button twice instead of once.
I am finishing the first part of comparison. You can expect a few more articles connected to that topic in the future. I think that if we talk about those two frameworks timing issues and page objects topics requires separate articles.