SE LE NI UM SI MPLI FI E D
CATEGORY ARCHIVES : WEBDR IVER
Absolute basics of Selenium WebDriver in Java onUdemy
I recently created a free Udemy course.
“Start Using Selenium WebDriver with Java”
You have to sign up to udemy.com to view the
full course, although I have uploaded some of
the videos to youtube (see below).
Udemy seemed like the fastest way to get the
material online in an organised fashion.
It covers the absolute basics of setting up an IDE, getting hold of the tools and libraries, and writing
your first test in WebDriver. These instructions go out of date quickly in a book so I hope that by
creating videos. I can keep them up to date easily.
I created this so that myself and other consultants have a resource to send people to for the basics. The
basic installs are simple to do, but can occupy a good 2 or 3 hours of an introductory course.
I hope that people can use this as a prerequisite to training. Certainly when you get me to train you, I
expect you to have done the steps in the Udemy course first, because then I know that the training
course can focus on adding the value that an instructor can add.
How to create a simple Selenium WebDriver test using Java and IntelliJ
FluentWait with WebElement
My synchronisation strategies with WebDriver have generally revolved around the WebDriverWait and
creating custom ExpectedCondition objects.
When I first saw the FluentWait, I dismissed it as a slightly more customisable version of the
WebDriverWait.
But then I looked more closely at the signature of the constructor, and I noticed the Generics being
used.
The use of Generics means I don’t have to pass in a WebDriver, I could use a WebElement, or a By, or
anything I want really.
Which could prove really handy for Ajaxy apps.
I’ve knocked up this little code example using countdown timer at stuntsnippets. I uploaded the
example page to this site at javascript_countdown
The code waits for the element to become visible using a WebDriverWait then drops down to a lower
level of granularity to check that the seconds count down to 04
WebElement countdown = driver.findElement(
By.id("javascript_countdown_time"));
new WebDriverWait(driver,10).
until(ExpectedConditions.visibilityOf(countdown));
new FluentWait<WebElement>(countdown).
withTimeout(10, TimeUnit.SECONDS).
pollingEvery(100,TimeUnit.MILLISECONDS).
until(new Function<WebElement , Boolean>() {
@Override
public Boolean apply(WebElement element) {
return element.getText().endsWith("04");
}
}
);
Note in the above code I tell FluentWait that it will be sending a <WebElement> through to the apply
method.
FluentWait can take a Predicate, or a Function, as an argument in the until method.
I chose to use a Function, which can return any object, I chose to return a Boolean.
A predicate will only return a boolean.
FluentWait can prove pretty handy for Ajax because of the flexibility you have for ignoring exceptions
and easily tweaking the wait and polling times.
You can learn about the differences between Function and Predicate from the guava-libraries
documentation.
I have uploaded the code for this example to bitbucket, in the uploaded code I also have an example of
using the Predicate.
Categorising WebDriver–Navigation, Interrogation,Manipulation
Hands up who uses generalisation in their modelling strategies? Me too!
I’ve worked through the WebDriver API in some detail and as part of figuring out a way to replay the
API back to people learning WebDriver. I have adopted 3 main classifications:
Navigation
Interrogation
Manipulation
My 4th categorisation has resulted in fewer methods:
Synchronisation
I initially expected to find more in the Synchronisation category, since the strategies around
synchronisation often consume a lot of automation time. But in retrospect it makes sense since
WebDriver abstracts the Driver and Dom interaction, not the application level semantics around
elegant progression.
Initially I used Navigation, Inspection, Interaction and Synchronisation but I didn’t want so many
beginning with the same letter. I find that bad for my mnemonics. So feel free to chop and change the
words as most appropriate for you.
In this post I will present an initial categorisation of portions of the API. I will not present the full
API.
At a high level we have the 5 main areas. What? First we had 3, then 4, now 5. Have I engaged you in a
Monty Python sketch?
Some of the areas of the API relate closely to specific domain elements that I have grouped them in an
easy to find categorisation first.
Navigation
Navigation has the smallest set of API elements for a high level category:
Again I probably should not have experienced surprise at finding a small set of API functionality, since
so much of navigation in automation involves manipulating the application under test.
Interrogation
Initially I had this as Inspect, influenced I suspect by Firefox and Chrome. But I moved to interrogation
because it sounds more macho.
Manipulation
WebDriver manages to squeeze a lot of bang for the buck out of its API with click and sendKeys doing
the vast bulk of the donkey work on HTML elements for us. We can drop down into the Advanced
User Interactions subset for more detailed work, and some support classes exist to help with Select
elements.
Pretty concise and easy to remember.
Synchronisation
Our tests become flaky and intermittent when we do not get our Synchronisation strategies correct.
We have many options open to us in terms of the semantics of Synchronisation but we tend to rely on
waiting for conditions to implement them.
WebDriver provides help in the support classes for this.
Built in, we have the implicit wait associated with some of the other API calls and with the Page
Objects, and while these can help us build tests quickly, we don’t want to rely on those long term.
Much remains for me to write on the topic of Synchronisation.
Domain
Hey look, a category that he doesn’t know how to categorise. Yup that happens. I value flexibility in
my modelling. when we expand this category you will find that I have subcategorised into the above
categories.
But there exist parts of the API that while not all together in the API, I found value in grouping in
terms of the domain.
Why?
This acts as a cheat sheet reminder of where to find stuff. I haven’t put all the params and return types,
because I can get all that with code completion.
The WebDriver API used to have a lot more complexity to it, but over the years the team have distilled
it into a pretty tight and fairly small set of methods.
I want it!
You can download the full mind map – should you want it by doing a ‘save as’ on webdriver.mm
I used FreePlane as the mind mapping tool.
You can also find the mindmap and images over at bitbucket, I even converted it to pdf for you
Advanced User Interactions and the expletivemachine
For the life of me, I could not get User Interactions to work properly. They just wouldn’t work. They
used to work, last time I tried them. But now…
new Actions(driver).keyDown(Keys.CONTROL).
sendKeys("b").
keyUp(Keys.CONTROL).
perform();
“!*&^*&£$”, as old comics used to illustrate the expletive.
I found I wasn’t alone on the forums.
I used a simple user interactions experimental page which had enough to let me see what was
happening:
key_click_display.html
Then I wrote the offending parts of the test code in a new test that I could run cross browser. You can
find it in the WebDriverExperiments over on bitbucket.
And lo, on my machine, with the way I was starting the browsers, with my version of Selenium and the
versions of the browsers I installed…
I got results I didn’t expect…
It worked.
“!*&^*&£$”, as old comics used to illustrate the expletive.
Wait a minute. Did you see that? That warning about firecookie now being part of firebug? I think
Firefox just updated itself !
Hmmm, let me check my smaller laptop – Hey, that is running Firefox 11.0. Blink. Hey, it just updated
to 14.0.1.
I had the most recent version of WebDriver, but an out of date version of Firefox.
OK, now I have to stop that happening ever again.
Lessons learned.
I better start reading the forums more closely. I bet someone already found this out.
Next time, when I update WebDriver, I have to force a manual download of the updated
version of Firefox. None of this relying on the automated update stuff. Who trusts
automation anyway?
Because sometimes it’s hard… some tips for“Working with WebDriver”
“They” should pass a law requiring all titles to utilise alliteration.
I can see why people find it hard to work with WebDriver, particularly people new to the tool. Over
the years I have learned to expect constant change from the Selenium toolset. After all, the ongoing
betterment of web automation worldwide must continue.
The API changes. Sometimes it works in this browser sometimes it doesn’t, sometimes a
browser upgrade breaks Selenium. Sometimes the version of Selenium has bugs etc. etc.
And throughout all of that, I have used it for production automation, without pulling my hair out too
much.
Herein, some approaches I have adopted:
Assume out of date published web documentation
Learn to read the source code. e.g. I just worked with the ChromeDriver. The code has deprecated
Capabilities in favour of ChromeOptions. The documentation only describes the capabilities, the code
describes the options. I read the code.
Get your tests working in multiple browsers
Create the ability to switch off some tests for certain browsers
I primarily want to test functionality, if I have specific functionality that I can’t check cross browser
with Selenium then I’ll do it manually. After all I automate a subset of functionality.
Learn JavaScript
Then you can use the JavascriptExecutor to provide a rich source of workarounds and augmentations.
A vital part of working with Selenium involves workarounds. Never assume that one perfect way of
doing anything exists. Do it quick and dirty if you need to. Selenium has a habit of making my
workarounds redundant on the next update.
Don’t try and automate everything
“But I really want to check that the div is displayed when Ctrl+Z are pressed down, and that it goes
when I key up” – great, then find another way to do it, or add a new library to your toolset.
If you find a problem, check the forums and bug reports
It may not just happen to you. Other people find problems and report them. Sometimes you will find
workarounds in the forum thread. You don’t want to spend hours chasing through your own code
when you might have stumbled over a known problem, but one that only impacts a small set of
fortunate people like yourself.
Create an experiment to isolate the problem
If it seems like some people have the problem, but others don’t. Then create the simplest experiment
that will demonstrate the problem. This will help you find a workaround. The fastest short term work
around I know of involves excluding it from one browser, and running it on another.
Sometimes you might care more than the rest of the world, so either try to fix WebDriver, or find a
workaround.
Check your synchronisation
Many (possibly most) of the problems I encounter in my and other people’s test code relate to
synchronisation. Learn to use the WebDriverWait rather than rely on implicit waits. Try your test in
debug mode, if it works there, but fails in the build, then investigate it as some sort of synchronisation
problem. Try running the test in a loop and see if you can get it to fail, this can help diagnosis.
JavascriptExecutor can help with synchronisation, if you learn to use it, then you’ll see the possibilities
when you need it.
Many different types of synchronisation problems exist – I include parallel running of tests which
interfere with each other, as a synchronisation problem.
Look for a bug in your test, or the app under test
Don’t stop investigating and fix the blame on WebDriver. People find it easy to do that, and then miss
problems of their own making. If your organisation seems to have a unique problem then assume the
fault rests with you and investigate fully.
A minimal WebDriver based DSL
I read on the Wikipedia entry for Selenium, that Selenium 2.0 aims to provide a base for your own
DSL (Domain Specific Language).
“What might such a language look like?” I thought to myself.
Well, I’ve build DSLs before, but I really thought “What might the minimal DSL that I could create
look like?”
And so, we proudly present. The Flue ntWebEl em e nt .
Yes, all you Seleniumists and Webdriverites, now you too can write:
myWeblement.clear().and().sendKeys(“woo, a new dawn”).then().submit();
OK, so you’d have to write a bit more. Let me list the full test:
private static WebDriver driver;
@BeforeClass
public static void setup(){
driver = new FirefoxDriver();
driver.get("http://www.compendiumdev.co.uk/selenium/search.php");
}
@Test
public void whatIfWeHadAFluentWebElementForSearchPage(){
FluentWebElement searchBox = new FluentWebElement(driver.findElement(By.name("q")));
searchBox.clear().then().sendKeys("Fluent Programming").and().submit();
assertTrue(driver.getTitle().contains("Fluent Programming"));
}
@AfterClass
public static void tearDown(){
driver.quit();
}
And how was this done?
If you look in the source code repository then you can see the test, and the FluentWebElement code.
Just a class that wraps the WebElement, delegating off to the WebElement most of the time and
returning self, for all those WebElement methods that normally void out.
e.g. the important bits
private WebElement webElement;
public FluentWebElement(WebElement aWebElement){
this.webElement = aWebElement;
}
// Fluentese
public FluentWebElement click() {
webElement.click();
return this;
}
public void submit() {
webElement.submit();
}
public FluentWebElement sendKeys(CharSequence... keysToSend) {
webElement.sendKeys(keysToSend);
return this;
}
public FluentWebElement clear() {
webElement.clear();
return this;
}
public FluentWebElement and(){
return this;
}
public FluentWebElement then(){
return this;
}
Warning: This has not been used in a production environment. This was a “What If ?”, fun experiment.
Sporting Event Tribute Using Selenium WebDriver
As I write this, the capital city of England currently hosts a large sporting event. I can mention no
names nor dates, for that sequence of words forms part of the protected thou shalt not use list. I don’t
even know if ‘they’ will allow me to link to that page.
Regardless, I created a small test app which pays tribute to sporting events through the years.
Since I grew up in the 80s, programming on ZX Spectrums, I have a fondness for scrolly text that
judders. I decided to replicate this effect using the browser title and hacking in my own text,
overwriting the official text in page <title/> element.
((JavascriptExecutor)driver).executeScript("document.title='" + displayBanner + "';");
I don’t do this a lot when writing automated tests, but I have to confess – I don’t let the application
functionality stopping me testing it. So I do sometimes force elements to be visible so that I can access
them through automation. I mainly do this for exploratory automation work.
If you have never done this before then I shall recommend now that you try it. Rule nothing out in
your hunt for test automation effectiveness.
I primarily wrote this little script to experiment with the WebDriver window positioning commands:
(WebDriver) .manage().window.setSize
(WebDriver) .manage().window().getPosition()
WebDriver) .manage().window().setPosition()
The listing from the video is below:
@Test
public void bounceThatWindow(){
WebDriver driver = new FirefoxDriver();
driver.get("file://" + System.getProperty("user.dir") + "/jsrunner.html");
driver.manage().window().maximize();
Dimension fullScreenSize = driver.manage().window().getSize();
int changeWidth = 200; int changeHeight = 210;
int xDir = 8; int yDir = 8; int xDirIncrement = xDir; int yDirIncrement = yDir;
driver.manage().window().setSize(new Dimension(changeWidth,changeHeight));
Point position = driver.manage().window().getPosition();
String banner = "***BANG****........ AND THEY ARE OFF........ Automation can be fun. " +
" EvilTester.com present a javascript and browser" +
" animation using Selenium 2 WebDriver tribute to the" +
" sporting event that cannot be named lest we be sued";
int bannerStart = 0;
for(int bounceIterations = 0; bounceIterations < 1000; bounceIterations ++){
position = position.moveBy(xDir,yDir);
driver.manage().window().setPosition(position);
if(position.getX()>(fullScreenSize.getWidth() - changeWidth)){ xDir = -1 * xDirIncrement; }
if(position.getX()<0){ xDir = xDirIncrement; }
if(position.getY()>(fullScreenSize.getHeight() - changeHeight)){ yDir = -1 * yDirIncrement; }
if(position.getY()<0){ yDir = yDirIncrement; }
String displayBanner = banner.substring(bannerStart,bannerStart+30);
((JavascriptExecutor)driver).executeScript("document.title='" + displayBanner + "';");
bannerStart++;
if(bannerStart > banner.length()-35){banner += banner;}
}
driver.quit();
}
Since I only planned to let the demo run for a short period of time you can see that I just let the banner
string extend and extend and extend by continually appending the text to it. Seemed like a nice sensible
shortcoming given the nature of the demo. Just a warning to you in case you decide to put this into a
loop for an hour or so.
You can find the full source listing at bitbucket https://bitbucket.org/ajrichardson
/webdriverexperiments
Specifically here.
The ascii animation was created using asciimator.net
A fun extension, should you want a challenge, would be to create 3 windows, size them, and then have
WebDriver play a game of PONG with them.
Enjoy.
I never expected ExpectedConditions
I was having a brief trawl through the Selenium codebase and noticed something I hadn’t seen before.
An ExpectedConditions class which exposes a bunch of static methods to save me having to write
code.
And it does save me having to write code.
For example in the past I would have written something like this:
driver.get("http://compendiumdev.co.uk/selenium/calculate.php");
new WebDriverWait(driver,10).
until(new TitleContainsCondition("Selenium"));
This opens the driver to the page and waits until the Title contains the text “Selenium”.
And I had to write code to support this i.e. my TitleContainsCondition class:
public class TitleContainsCondition implements ExpectedCondition {
private String subMenuText;
public TitleContainsCondition(final String subMenuText) {
this.subMenuText=subMenuText;
}
@Override
public Boolean apply(@Nullable WebDriver webDriver) {
return webDriver.getTitle().contains(this.subMenuText);
}
}
But now, I don’t have to, the ExpectedConditions class has a method for this common contingency.
driver.get("http://compendiumdev.co.uk/selenium/calculate.php");
new WebDriverWait(driver,10).
until(ExpectedConditions.titleContains("Selenium"));
I used a similar approach in my production code, I typically have a factory for the ExpectedConditions
so that I can write.
driver.get("http://compendiumdev.co.uk/selenium/calculate.php");
new WebDriverWait(driver,10).
until(WaitFor.titleContainsCondition("Selenium"));
I’ll keep my WaitFor Class hanging around because it still does things that are not common to most
folk, since we test applications which are domain specific.
And if you haven’t noticed the ExpectedConditions before, now might be a good time to try them out.
You can find the full source code for the examples on bitbucket.
link to official docs
How to stop firefox ‘update failed’ dialog messingwith your WebDriver automation
There I am, figuring out how to debug my FitNesse automation from within eclipse. And up pops the
Firefox ‘update failed dialog’ and interfering with my automation.
A bane and a pain when using Selenium RC. But with WebDriver there are easy ways round this.
Start firefox with a profile and set the "app.update.silent" firefox property to true.
The update error will still happen, but at least firefox won’t try and tell your automated processes
about it.
profile.setPreference("app.update.silent", t r ue );
For more details visit http://kb.mozillazine.org/Category:Preferences
Oh, and the FitNesse in unit tests is http://fitnesse.org/FitNesse.UserGuide.RunningFromJunit
e.g.
@Test
public void runAFitNesseTest(){
JUnitHelper helper = new JUnitHelper("./fitnesse",
new File(System.getProperty("System.java.io.tmpdir"), "fitnesse").getAbsolutePath());
// type in the name of the test you want to debug here
String testName = "FitNesse.AcceptanceTestSuite.ATestCase";
try {
helper.assertTestPasses(testName);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Selenium 2.0rc2 – Hey, where’s my unashamedlyugly wait
Warning, this blog post contains Java code. Some testers may want to look
away now.
I was quite happy using the unashamedly ugly Selenium Wait class, for adhoc waits that I did not want
to refactor into proper classes.
e.g.
new Wait("JS Page title did not change"){
@Override
public boolean until() {
try{
return
driver.getTitle().startsWith(TITLE_OF_PAGE_STARTS_WITH_THIS);
}catch(Exception e){
// ignore not found exception
}
return false;
}
};
I upgrade to Selenium 2.0rc2 and what happens?
Gone!
“Pah,” thinks me, “no-one’s gonna stop me writing pug ugly waits”:
new WebDriverWait(driver, 10){
}.until(new ExpectedCondition<Boolean>(){
@Override
public Boolean apply(WebDriver driver) {
return
driver.getTitle().startsWith(TITLE_OF_PAGE_STARTS_WITH_THIS);
}});
There you go. Still Ugly.
But Wait…
now we only have one wait construct to deal with,
this makes it easier to refactor the expected conditions out
Refactor it out?
Yup, we don’t let it stay ugly, particularly not if we use the same wait somewhere else.
We might create an ExpectedConditionFactory…
e.g.
ExpectedConditionFactory weCanSee = new ExpectedConditionFactory();
Which looks a bit like…
public class ExpectedConditionFactory {
public ExpectedCondition<Boolean> pageTitleStartsWith(
String titleOfPageStartswith) {
return new PageTitleStartsWithExpectedCondtion(titleOfPageStartswith);
}
}
So the calling code looks more like…
new WebDriverWait(driver, 10){
}.until(weCanSee.pageTitleStartsWith(TITLE_OF_PAGE_STARTS_WITH_THIS));
Then create a new class that implements the ExpectedCondition…
public class PageTitleStartsWithExpectedCondtion implements
ExpectedCondition<Boolean> {
private String titleOfPageStartsWith;
public PageTitleStartsWithExpectedCondtion(String titleOfPageStartsWith) {
this.titleOfPageStartsWith = titleOfPageStartsWith;
}
@Override
public Boolean apply(WebDriver driver) {
return driver.getTitle().startsWith(titleOfPageStartsWith);
}
}
And I might even revisit the original “new WebDriverWait…” code and have:
WebDriverWait wait = new WebDriverWait(driver,10);
wait.until(weCanSee.pageTitleStartsWith(TITLE_OF_PAGE_STARTS_WITH_THIS));
And then I can tuck the declaration of the wait up in the Page Object constructor so I just see the tidy
wait.until part in my Page Object methods
Yes, sadly the end result does not look ugly at all. Some people might almost call it readable.
wait.until(weCanSee.pageTitleStartsWith(TITLE_OF_PAGE_STARTS_WITH_THIS));
Powered by WishList Member - Membership Site Software
But it does require more code, so you boost your “lines of test code written per day” metric.
Bonus.
Feel free to constructively mock my code in the comments – so as I can
learn how to code more good like.