web driver selenium simplified

21
SELENIUM SIMPLIFIED CATEGORY ARCHIVES: WEBDRIVER Absolute basics of Selenium WebDriver in Java on Udemy 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

Upload: vikas-singh

Post on 15-Aug-2015

42 views

Category:

Technology


4 download

TRANSCRIPT

Page 1: Web driver selenium simplified

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

Page 2: Web driver selenium simplified

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"));

Page 3: Web driver selenium simplified

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

Page 4: Web driver selenium simplified

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

Page 5: Web driver selenium simplified

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.

Page 6: Web driver selenium simplified

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.

Page 7: Web driver selenium simplified

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.

Page 8: Web driver selenium simplified

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).

Page 9: Web driver selenium simplified

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?

Page 10: Web driver selenium simplified

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

Page 11: Web driver selenium simplified

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

Page 12: Web driver selenium simplified

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

Page 13: Web driver selenium simplified

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.

Page 14: Web driver selenium simplified

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

Page 15: Web driver selenium simplified

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

Page 16: Web driver selenium simplified

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.

Page 17: Web driver selenium simplified

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());

Page 18: Web driver selenium simplified

// 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?

Page 19: Web driver selenium simplified

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);

}

Page 20: Web driver selenium simplified

}

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));

Page 21: Web driver selenium simplified

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.