Your cool PS I'm Sebastian
alright big fella, how's spain? when are we going to play drums together again?
YOU ARE THE REASON
trying on a metaphor
TVSTRANGERTHINGS
ojovivo

roma★
Monterey Bay Aquarium
"I'm Dorothy Gale from Kansas"
No title available
I'd rather be in outer space 🛸
d e v o n
Misplaced Lens Cap

tannertan36

Kaledo Art

Product Placement

#extradirty
Claire Keane

Discoholic 🪩

ellievsbear
No title available
h
seen from Netherlands

seen from United States

seen from Malaysia
seen from Indonesia

seen from Serbia

seen from United Kingdom

seen from Japan
seen from United States
seen from Spain
seen from United States
seen from Iceland

seen from Finland

seen from United Kingdom
seen from Australia

seen from United States
seen from China

seen from United States

seen from Brazil
seen from Australia
seen from Australia
@iainrose
Your cool PS I'm Sebastian
alright big fella, how's spain? when are we going to play drums together again?
Naming convention for By object locators
I need some help, I've been using the following naming convention for my locators and it doesn't feel exactly right yet.
By myElementLocator = By.id("myElementId"); WebElement myElement = driver.findElement(myElementLocator);
It seems a bit redundant using the xxxxLocator suffix in the variable name for my By objects. It's a By object after all, what else would it be except a locator?
However I still need to differentiate between the element and locator. Can anyone suggest a better naming convention?
No periods in ID's, period.
I've been asked by my dev team if there are any rules or guidelines on naming conventions for ID's on WebElements. I'm sure there are but I can't be bothered to google that right now.
One thing I do know, is that in the past I've run into ID's that contain periods which really screw up my css locators.
Example:
<div id="user.name"> .... </div>
You'd just use the ID in the real world but for the sake of an example, trying to create a css locator for this div might look like this,
#user.name
except that's not going to work as it will be looking for something like this
<div id="user" class="name"> ... </div>
Probably falls into the 'bleeding obvious' category of advice but I've seen this enough times to warrant this post.
No ID? You're not coming in
A quick glance at the WebDriver google group will confirm 2 things for you really quickly
There are a LOT of people using that group
There are a LOT of people still using some really dirty, brittle & possibly auto-generated xpath or css locators in their scripts
Brittle locators might be being used by ignorance or necessity.
In the case of the former, start with Santi from SauceLabs' excellent posts here and here then check out his video here too.
In the case of the later, Adam Goucher recently posted about this on his blog and suggested that if the project team aren't willing to support the automation effort and add ID's when needed it might be is time to pull the plug.
Luckily, I haven't run into too many situations where this has happened but I have had this conversation more than once,
Me: Can you put some ID's on this for me so I can automate it
Dev: Sure, what exactly would you like
Me: errrrr ...
To avoid this embarrassment, I started putting together a list of ways our dev team can make the application easier to automate.
Today, I'll talk about how we mark up tables and define a locator strategy to crawl through a table and grab whatever we need.
Tables:
It's usually always better not to reinvent the wheel so I looked around for some standards on how to markup tables and found this at W3Schools.
Wicked, let's start with that. Use the ID of a column in the table header as the header attribute of the same column in the table body.
How would that look for a simple users table?
<table id="usersTable"> <thead> <th id="name"> Name </th> <th id="email"> Email </th> <th id="likes_owls"> Do you like Owls? </th> </thead> <tbody> <tr> <td headers="name"> Iain </td> <td headers="email"> [email protected] </td> <td headers="likes_owls"> Yes </td> </tr> <tr> <td headers="name"> Mr. Bastard </td> <td headers="email"> [email protected] </td> <td headers="likes_owls"> No </td> </tr> </tbody> </table>
This allows us to define the locators we need for this table like this,
By usersTableLocator = By.id("usersTable"); By nameLocator = By.css("td[header='username']"); By emailLocator = By.css("td[header='email']"); By likesOwlsLocator = By.css("td[header='likes_owls']");
Using these locators allows us to grab all the WebElements in a particular column of the table body by doing something like this,
List<WebElement> names = driver.findElements(nameLocator);
Awesome, we can iterate over that and find what we need but now what if we want to grab a specific field in a specific row in the table? Oh yeah, we also need this to work no matter how many rows exist in the table and no matter what order the rows are listed.
In this case, I'd go back to the dev team and ask them to add the unique identifier of the artefacts presented in the table (in this case 'users') as the ID of each <tr> element in the table body. The unique identifier for users is usually userId or similar so let's use that in our example.
<table id="usersTable"> <thead> <th id="name"> Name </th> <th id="email"> Email </th> <th id="likes_owls"> Do you like Owls? </th> </thead> <tbody> <tr id="userId_1"> <td headers="name"> Iain </td> <td headers="email"> [email protected] </td> <td headers="likes_owls"> Yes </td> </tr> <tr id="userId_2"> <td headers="name"> Mr. Bastard </td> <td headers="email"> [email protected] </td> <td headers="likes_owls"> No </td> </tr> </tbody> </table>
Now we can create a WebElement representing any field in any row of of the table and interact with it however we want.
// Create a WebElement representing the whole table WebElement usersTable = driver.findElement(usersTableLocator); // Create a WebElement representing the row containing userId_1 WebElement rowForUser1 = usersTable.findElement(By.id("userId_1")); // Create a WebElement representing the email field for userId_1 WebElement emailForUser1 = rowForUser1.findElement(emailLocator); // Get the email for userId_1 String email = emailForUser1.getText();
Using this approach has meant that the work required by the development team to support the automation effort is kept to a minimum and we also create (and maintain) the minimum number of locators we need for navigating the table.
The developers only have to add ID's to
The table itself
Each <th> element in the table header
The header attribute of each <td> element in the table body
Each <tr> element in the table body
The testers only have to create locators for
The table itself
Each column in the table body
This works pretty well for my team and as it's basically just following some published web standards the developers are happy to add the ID's I need as a result. It's a double win, my automation is easier and the developers look like studs in their code review.
How are the rest of you doing this?
Using By objects in canned WebDriverWait ExpectedConditions
Closing up my trilogy of rants on ditching the PageFactory, here's the final example.
Synchronizing your web application and using WebDriverWait effectively is one of the most important skills you will need to learn.
Using implicit waits may seem like a good idea at first but if you're using a 30s implicit wait and need to verify the absence of a WebElement on a page you're effectively adding a 30s static pause into your script every time you do this. If you need to verify the absence of multiple WebElements your tests can quickly become dogs that take minutes to run instead of seconds.
To avoid this, we use explicit waits whenever we need to synchronize our web application.
Luckily, the nice people on the WebDriver project have provided us with 17 canned WebDriverWait ExpectedConditions that should cover the majority of what we need.
Take a closer look however and you'll see that more than half of these take By objects as an parameter. If you think you'll ever need to use these (hint. you will) ditch the PageFactory and store your locators as By objects from the start.
For the remaining conditions that take WebElement objects as a parameter, you can still create a WebElement using your By object locator and pass that in.
By myElementLocator = By.id("myElementID"); WebDriverWait wait = new WebDriverWait(driver, 120); // Canned wait example using By object as a parameter wait.until(ExpectedConditions.invisibilityOfElementLocated(myElementLocator); // Canned wait example using WebElement as a parameter WebElement myElement = driver.findElement(myElementLocator) wait.until(ExpectedConditions.stalenessOf(myElement);
This gives you access to the full suite of canned ExpectedConditions and you'll drastically reduce the number of custom ExpectedCondition methods you need to create yourself.
Using By objects to locate WebElements inside other WebElements
Continuing my examples on reasons to use By objects as your locators and ditch the PageFactory, here's the second example.
Suppose you have some drag and drop functionality on your page, using the PageFactory you might define your Page Object something like this.
@FindBy("dragZoneId") WebElement dragZone; @FindBy("draggableItemId") WebElement draggableItem; @FindBy("dropZoneId") WebElement dropZone;
This will allow you to locate the draggable item wherever it is, but what if you want to assert that the draggable item is located in the drag zone on page load and in the drop zone after a drag & drop action?
There's no clean and easy way to do this as you cannot pass a WebElement into any method you can call on another WebElement.
However, as I'm sure you can guess, using By objects as your locators makes this simple.
By dragZoneLocator = By.id("dragZoneId") By draggableItemLocator = By.id("draggableItemId") By dropZoneLocator = By.id("dropZoneId") public boolean isItemInDragZone() { WebElement dragZone = driver.findElement(dragZoneLocator); return dragZone.findElements(draggableItemLocator).size() > 0; } public boolean isItemInDropZone() { WebElement dropZone = driver.findElement(dropZoneLocator); return dropZone.findElements(draggableItemLocator).size() > 0; } WebElement draggableItemWhereverItIs = driver.findElement(draggableItemLocator);
Using By objects to verify if WebElement present
In a previous post, I recommended not using the PageFactory helper class in WebDriver as it can restrict the reuse of your locators.
Here is a more detailed explanation of the first example, verifying the presence or absence of WebElements
If you use the PageFactory to define your WebElements, you'll do something like this.
@FindBy(id = "myElementID") WebElement myElement;
The problem with this is that there is no method you can call on that WebElement to assert if it doesn't exist on the page.
In fact, if you try to call any method on that WebElement and it is not present in the browser, you'll get a noSuchElementException.
For example, this will return true if the WebElement is visibile but a noSuchElementException if it is not present at all.
@FindBy(id = "myElementID") WebElement myElement; myElement.isDisplayed();
The WebDriver javadocs recommend using the FindElements() method when looking for non-present elements.
findElement should not be used to look for non-present elements, use findElements(By) and assert zero length response instead
Another glance at the javadocs will show that the findElements method takes a single By object as a parameter, so if you use the PageFactory you'd need to duplicate the locator of that WebElement and store it as a By object anyway.
By saving our locator as a By object in the first place, we can still create a WebElement whenever we need to using the findElement method and we can reuse that same locator to assert the presence or absence of a WebElement.
It is a bit more code to do it this way, but I'd take a bit more code using core WebDriver functionality over duplicated locators or fugly try / catch blocks any day.
By myLocator = By.id("myElementID") public boolean isElementPresent(By locator) { return driver.findElements(locator).size() > 0; } if (isElementPresent(myLocator)) { WebElement myElement = driver.findElement(myLocator); myElement.click() }
Should testers know how to code?
While at CAST2012 this week, I quoted something on twitter that Adam Goucher said in his conference opening session.
This is the last generation of testers that don't know how to code
Obviously, commenting on the future of testers whilst at a testing conference opened up a (mostly) good natured discussion on the topic. This came up again during Elizabeth Hendrickson's keynote so clearly I wasn't the only one thinking about this.
After a few days to absorb the various counterpoints to this viewpoint, I've come to the conclusion that I still do think it is the last generation of testers that don't know how to code. However, I think it's also the last generation of milkmen, postmen, bartenders & lawyers that don't know how to code too.
I believe that this internet thing is going to be HUGE, once it takes off and more than just the rich & elite have an email address, facebook / twitter accounts or web enabled mobile devices it's only a matter of time until kids get taught basic coding skills at elementary school (if they're not already) so naturally the next generation of testers will know how to code.
Of course, knowing how to code is not the same as being good at coding but that's another story. It's also not the same as coding on a daily basis in your day job. It's also not the same as being expected to push any product code to production while working as a "tester". However, for the sake of argument let's assume that most good, skilled and conscientious testers would probably take the time to make some effort to ensure they have some idea of what they are doing before introducing this into their work environment.
Testing is a difficult job, make no mistake about that and I do not think that test automation will ever remove the need for manual testing. However, there are certain mundane, repetitive tasks (such as regression testing / checking) commonly owned by testers that should be automated and doing so will free up more time for manual testing.
I also think that good, skilled testers are intelligent and adaptable people that should not feel threatened by test automation, as learning one new skill will not push two existing skills out of their brain.
Learning to code will not stop you from being an effective manual tester, I think it will actually make all testers better at manual testing as the process of learning a little bit about how the products of the industry we work in are made will make us more effective at testing.
Talking / listening to developers in a language they can understand, writing concise bug reports that get the message across quickly but without prejudice and most importantly correcting the common attitude misalignment amongst some testers about "how could that lazy / incompetent / sloppy developer make such a simple (in your mind) mistake" has got to be a good thing.
Time to evolve.
Don't feel bad for me
I'm currently using Selenium with Java. There, I said it. I know it's not what the cool kids are doing but I don't care.
My project is not a Java project. It's also not a Selenium project. It's a test project and it's purpose is to help me spend as much time doing manual exploratory testing as possible as that's how I find most bugs in new and updated code.
On my trip to California this week for CAST2012, two Selenium meetups and last but not least some real work at xMatters HQ I've lost count of how many conversations I've had that went something like this .....
Hey, what are you using for your Selenium bindings?
Java
Oh, I feel bad for you
I really don't care. My decision to use Java was not based on what I personally think is the most interesting language, that would probably be Python. It was also not based on what I have the most experience with, I started using open source test tools in 2004 when I found WATIR and probably have the most experience in Ruby. I used Java as I wanted the ENTIRE team (development and testing teams) to contribute and this cast the widest net.
Plus, I along the way I've found out that ...
There is more documentation for Selenium in Java that any other language
TestNG provides a simple way to group my tests. In Ruby, Test::Unit doesn't support this and I'm not sold on the need for RSpec. Simple assertions are all I need although I know py.test offers this too.
Gradle has allowed me to define my test jobs in a really clean and simple DSL without any xml, plus it manages all my dependencies for me, lets me run my tests in parallel with 1 line of code and builds my IDE project with one command too.
InteliJ does most of the heavy lifting for me and as a tester doing automation development not a developer doing test automation I kinda like having code completion and don't need to be knee deep in vim (yet) to feel good about my craft.
So while I understand that Java isn't the coolest kid in town, it was the right tool for the job at hand and I don't need you to feel bad for me.
Check out my sample build.gradle for a demo
Closing the (Page)Factory
Page Objects rock the party. No news there, they're awesome and most people using Selenium already know about them and have been using them for much longer than I have.
I learnt the basics about Page Objects from the Selenium Wiki. It's the best place to start and does a great job of explaining the concept but it's not perfect.
The main complaint I have about this tutorial is that the locators are saved inline in the loginAs example method, this will quickly lead to duplication of locators within the Page Object and is not ideal.
driver.findElement(By.id("username")).sendKeys(username); driver.findElement(By.id("passwd")).sendKeys(password); driver.findElement(By.id("login")).submit();
It does however, link to the PageFactory wiki page which looked like a good idea to me so when I started my latest job sometime last year (my first new Selenium project since the Selenium 2 release) I quickly implemented the page factory along with my page objects ..... which turned out to be one of the two biggest mistakes I made early in the project which I'm still in the process of undoing.
@FindBy(id = "myElementId") WebElement myElement;
On first inspection it looks like a win, less boiler plate code == good, right? Well no, and here's why.
You cannot use WebElements created using the PageFactory to assert the absence of an element without wrapping a try catch block around them.
You cannot use WebElements created using the PageFactory to search within other WebElements.
You cannot use WebElements created using the PageFactory in all of the canned WebDriverWait ExpectedConditions (which also rock the party in a big way)
So what's the alternative?
By myElementLocator = By.id("myElementId")
Save your locators as By objects
This allows you to solve all the problems listed above and I've also noticed a reduction in the number of StaleElementExceptions I run into as I have a lot more control about when an element is looked up and assigned to a variable.
My example of how I think a sample page object should look can be found here.
VanQ Selenium Presentation
I made the trip from Vancouver Island to BCIT back in May to present at that months VanQ meeting.
It was my first time presenting in public and overall was a really positive experience. The 60+ crowd was informed, enthusiastic and the content was well received.
The topic was how to combine Selenium with other tools to create your automation framework along with a few tips and pointers on how to create your tests, page objects, locators & wait conditions.
The guys from PQA were in attendance, thanks Pat for the photo.
Slides: http://bit.ly/vanq
Code: http://github.com/iainrose/vanq-java