Would you like to automate your acceptance tests against multiple browsers and multiple servers? How about make your UI tests run faster? And remove the boilerplate on them? Browser automation tools to the rescue! In this session, I'll share how you can gain back development time by using FluentLenium, an open source Java wrapper around the Selenium API. We'll take a brief look at what is new in the UI testing javascript ecosystem and then I’ll share with you some rules for writing better UI tests.
69. Scenario: Search by topic
Given there are 240 courses which do not
have the topic "biology"
And there are 2 courses A001, B205 that
each have "biology" as one of the topics
When I search for "biology"
Then I should see the following courses:
| Course code |
| A001 |
| B205 |
70. Feature: basic test
Scenario: scenario 1
Given feature I am on the first page
When feature I click on next page
Then feature I am on the second page
71. @Given(value = "feature I am o
the first page")
public void step1() {
this.initFluent();
this.initTest();
goTo(page);
}
72. @When(value = "feature I
click on next page")
public void step2() {
this.initFluent();
this.initTest();
click(“a#linkToPage2"
);
}
73. @Then(value = "feature I am on
the second page")
public void step3() {
this.initFluent();
this.initTest();
page2.isAt();
}
Hello, Thanks for coming.My name is Mathilde Lemee, I’m a java developper.I’m a QA Engineer for terracotta, a company specialized in in-memory caching and scaling. I’m also a Java User Group founder in France, one that focus women in Java and I do some mobile developpement on iPhone and Android - some kid apps to help children learning. Today we will talk about a fluent wrapper in top of the Java API, FluentLenium.
, I suppose that every body already know that selenium, stop me if it’s not the case. So Selenium is a great tool for testing UI, it’s open-source, stable, it supports a lot of browsers.Selenium WebDriver brings us a lot of new features, it’s now really much faster than before with selenium RCYou also have no need to run a separate server except on special cases (if you want to use Selenium Grid).Gone are wait_for_page_to_load(), wait_for_element_present(), etc. All element interactions, clicks, etc. are blocking now, which is good. We also have some tool to use the Page object Pattern, that is a pattern that everybody should really know.
Another thing is that Seleniumis also available in a lot of langages. Let’s do a small poll. How many of you are using the Selenium .NET API ? Python ? Ruby ? Java ? Perl ? PHP ? Another one ?So that’s pretty cool. But as a Java user, sometimes, the JAVA api is not enough. The way to deal with AJAX stuff for example, the API don’t really fit my need so I try to look about other API than just the java API. The first I found was Geb.
The first is GEB. GEB is a groovy wrapper around Selenium API.It brings together the power of WebDriver, the elegance of jQuery content selection, the robustness of Page Object modelling and the expressiveness of the Groovy language.It can be used for scripting, scraping and general automation — or equally as a functional/web/acceptance testing solution via integration with testing frameworks such asSpock, JUnit & TestNG.
Browser.drive { go "http://google.com/ncr" assert title == "Google” $("input", name: "q").value("wikipedia”) waitFor { title.endsWith("Google Search") }deffirstLink = $("li.g", 0).find("a.l") assertfirstLink.text() == "Wikipedia” firstLink.click() waitFor { title == "Wikipedia" }
The second lib I found after GEB was, Capybara is a ruby library. It can talk with many different drivers which execute your tests through the same clean and simple interface. You can seamlessly choose between Selenium, Webkit or pure Ruby drivers.Tackle the asynchronous web with Capybara's powerful synchronization features. Capybara automatically waits for your content to appear on the page, you never have to issue any manual sleeps.
def login! within("//form[@id='session']")do fill_in'Login', :with=>'user@example.com' fill_in'Password', :with=>'password' end click_link'Sign in' end
So with all of that, I try to look about what we have and what the teams I work in needed. First thing is Java. We are Java developpers and for some reason we cannot switch the langage we use. We also are front dev so javascript is something we also manipulate a lot. And with Javascript come JQUEry. And with Jquery, also CSS, because jquery use a css like syntax for selecting the elements. So we put all of that in a shaker, shake a little bit and FluentLenium is born.
So we put all of that in a shaker, shake a little bit and FluentLenium is born.
As we discuss, most of the java/javascriptdev are fluent with the css syntax because it’s really easy to understand, rules are clear and you can do most of the stuff you want. So to target a element in the dom, just use find with the css selector. Just a remember on CSS, # means the element that have the id.“a” means the balise (traduction) so all link in the page.myClassneabs the elements that have the class myClass
But because every people has a good level in css, we offer a fluent API to filter the dom elements. For exemple,, here, you select all the DOM element that havethe class small and the name foo
So, we know how we can have access to the dom element.
So, we know how we can have access to the dom element.
So, we know how we can have access to the dom element.
So, we know how we can have access to the dom element.
So, we know how we can have access to the dom element.
So, we know how we can have access to the dom element.
If you use the singular (getText instead of getTexts), it will automaticall take the text of the first element on the list.So you have getText but also getName, getId, getValue, getAtribute…
If you use the singular (getText instead of getTexts), it will automaticall take the text of the first element on the list
But now, how can we interact with the elements ?
ill("input").with("bar") or find("input").text("bar") will fill all the input elements with bar. If you want for example to exclude checkboxes, you can use the css filtering like fill("input:not([type='checkbox'])").with("tomato"), you can also use the filtering provided by FluentLenium fill("input", with("type", notContains("checkbox"))).with("tomato")fill("input").with("myLogin","myPassword") will fill the first element of the input selection with myLogin, the second with myPassword. If there are more input elements found, the last value (myPassword) will be repeated for each subsequent element.Don't forget, only visible fields will be modified. It simulates a real person using a browser!
So now, we know the basics of the API, how we use another browser but that will not help us to write readable tests. Writing readable tests should always be in our mind when we are writing tests, no matter the tests. Because you can have a the better API in the world, if your test look like that => Clique suivant
, you will have some troubles in the near future. So let’s go back with our Page Object Pattern.
Selenium tests can easily become a mess. To avoid this, you can use the Page Object Pattern. Page Object Pattern will enclose all the plumbing relating to how pages interact with each other and how the user interacts with the page, which makes tests a lot easier to read and to maintain.It will really help you to have tests that are readable and maintenable.Try to construct your Page thinking that it is better if you offer services from your page rather than just the internals of the page. A Page Object can model the whole page or just a part of it.
It's simplest to think of the methods on a Page Object as offering the "services" that a page offers rather than exposing the details and mechanics of the page. Usually, a page object should not have any assertions on it. In fact, it’s hiding the internals of the page (like the id of the dom element, the dom interactions).
To construct a Page, extend org.fluentlenium.core.FluentPage. In most cases, you have to define the url of the page by overriding the getUrl method. By doing this, you can then use the goTo(myPage) method in your test code.It may be necessary to ensure that you are on the right page, not just at the url returned by getUrl. To do this, override the isAt method to run all the assertions necessary in order to ensure that you are on the right page. For example, if I choose that the title will be sufficient to know if I'm on the right page:
Create your own methods to easily fill out forms, go to another or whatever else may be needed in your test.
And that’s the corresponding test.
But it can be simpler. Within a page, all FluentWebElement fields are automatically searched for by name or id. For example, if you declare a FluentWebElement named createButton, it will search the page for an element where id is createButton or name is createButton. All elements are proxified which means that the search is not done until you try to access the element.
So now, that we know how to create test using the page object pattern, change the browser and manipulate the dom using the API, we should speak about AJAX call. Because asynchronous call is something really common. Your first option is to defined global timeout. The first one withDefaultSearchWait will define the max time used to search for an element in the page (equivalent of driver.manage().timeouts().implicitWait(). The second, withDefaultPageWait, will defined the maximum time for a page to be loaded.But even if defined global timeout can fix a timeout problem, sometimes, we need to be more precise.
startsWith, notStartsWith, endsWith, notEndsWith, contains,notContains, equalTo, containsWord.And you can also define the polling if needed with pollingEvery.
startsWith, notStartsWith, endsWith, notEndsWith, contains,notContains, equalTo, containsWord.And you can also define the polling if needed with pollingEvery.
startsWith, notStartsWith, endsWith, notEndsWith, contains,notContains, equalTo, containsWord.And you can also define the polling if needed with pollingEvery.
So what’s about the page object pattern ? Because everything is made to
You can set the timeout in seconds for the page to throw an error if not found with@AjaxElement(timeountOnSeconds=3) if you want to wait 3 seconds. By default, the timeout is set to one second.
And you have a lot of other features, you can of course execute some javascript using the executeJavascript method, take screenshot when you want or when a test fail with jUnit for example, and something really important for us : you ALWAYS have access to the selenium driver, so we guarantee that everything you do with that driver, you will be able to do inside FluentLenium
We also have the isolated test, where tests are isolated. They do not depends on a FluentTest, you can embed everywhere.
You can also give as a parameter a driver, which is a way (there are a lot) to play the same tests with differents browsers.
At the beginning, one browser will be launch by test. It’s a godd way to avoid unexpected collisions between the test but because of that, it was
Now Use the class annotation @SharedDriver and you will be able to defined how the driver will be created :@SharedDriver(type = SharedDriver.SharedType.ONCE)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all classes and methods.@SharedDriver(type = SharedDriver.SharedType.PER_CLASS)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all methods on a same class.@SharedDriver(type = SharedDriver.SharedType.PER_METHOD)will allow you to create a new driver for each method.
Now Use the class annotation @SharedDriver and you will be able to defined how the driver will be created :@SharedDriver(type = SharedDriver.SharedType.ONCE)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all classes and methods.@SharedDriver(type = SharedDriver.SharedType.PER_CLASS)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all methods on a same class.@SharedDriver(type = SharedDriver.SharedType.PER_METHOD)will allow you to create a new driver for each method.
Now Use the class annotation @SharedDriver and you will be able to defined how the driver will be created :@SharedDriver(type = SharedDriver.SharedType.ONCE)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all classes and methods.@SharedDriver(type = SharedDriver.SharedType.PER_CLASS)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all methods on a same class.@SharedDriver(type = SharedDriver.SharedType.PER_METHOD)will allow you to create a new driver for each method.
Now Use the class annotation @SharedDriver and you will be able to defined how the driver will be created :@SharedDriver(type = SharedDriver.SharedType.ONCE)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all classes and methods.@SharedDriver(type = SharedDriver.SharedType.PER_CLASS)will allow you to use the same driver for every test annotate with that annotation (it can also be on a parent class) for all methods on a same class.@SharedDriver(type = SharedDriver.SharedType.PER_METHOD)will allow you to create a new driver for each method.
Cucumber is a tool that executes plain-text functional descriptions as automated tests. While Cucumber can be thought of as a “testing” tool, the intent of the tool is to support BDD. This means that the “tests” (plain text feature descriptions with scenarios) are typically written before anything else and verified by business analysts, domain experts, etc. non technical stakeholders.
So the non technical people will be able to write a lot of tests like this. You will have to defined what we call the “step” to make the bridge between the plain text line and the action.