Running on automatic

First technical post! I've dabbled in Selenium in the past, but not with huge success - I was trialling it on a big project and had the scripts running beautifully on the static HTML, but when we switched to the CMS-driven version it unexpectedly blew all the references out of the water and all of the scripts would have needed rewriting - at the time some other issues came up on the project and the budget was diverted to cover that and the automation fell to one side. Shame.

I've recently picked up a project which is a better fit, and not too ambitious - just automating submission of one form, to be used across several environments. As I've seen before with Selenium, there's a lot of learning as you go through so I thought I'd get some of them posted before I forget! I'm running WebDriver with Python, using both geckodriver for Firefox and chromedriver for Chrome.

1) Google is my friend

There are a ton of resources out there, and most of the issues I've come across have already been solved by someone else (and often in a more elegant fashion that anything I could come up with). Often there is more than one solution which also allows me to expand my python knowledge along the way. I don't advocate blind cut and paste though - whenever I've just thrown in some code without fully understanding the methods involved, I've always ended up being bitten when a similar problem arises elsewhere.

2) Comment, comment, comment

You cannot put too many comments in your code. Since my coding method contains a large dose of "try it and see what happens", I often have lines of code that are just trials rather than final code, and replace an existing working line - without commenting it's really easy to lose track of which lines actually do work in case I need to double back to an older version.

3) Use print statements to debug

I run the scripts from the command line (having typed up in Notepad++ - I'm old school and started coding before SDKs were even a thing). So when something is failing but I don't understand why I just add a bunch of print statements to pump out anything relevant - as soon as that doesn't match what I expect I know where the trouble is. For example

print("Selected HTML: ", testitem.get_attribute("innerHTML")) 

will show you what the inner HTML of the currently selected item is - this has saved me more than once when I thought I was selecting the correct item but wasn't.

4) Break down the route to an element

This is one of the most useful things I've found - I had a problem where the button I was selecting did not have any specific identifiers and was repeated on the page, so the search by id was giving the wrong instance. Based on previous experience I wanted to avoid going straight to the xpath route (as that may well break when you try to apply the test to a different environment, or if any small changes are made to the content on the page). Rather than trying to reach the item in one statement, I broke it down into smaller contexts which ensured that the correct instance of the button was selected, so instead of:

email = driver.find_element_by_id("Email")

(which returned the wrong instance of the Email field) I used

formLocator = driver.find_element_by_css_selector("ol.myform")
email = formLocator.find_element_by_id("Email")

This reduces the reliance on the css selector, as it just needs to be accurate enough to get you into the correct section of the page - I can then locate the actual field using the more reliable element ID. It also helps when you are trying to perform multiple actions in the same section of the page as you can then reuse the context "formLocator" to make sure you're in the right place.

There are probably better ways of handling this (to be honest, multiple fields with the same identifier should really be fixed by the developers, but the horse has bolted on that one for this project) - I think this is making the best of a bad situation.

5) Throw a known failure in to stop the script running

Again, there may be a better way of doing this, but I found that adding a search for an item that doesn't exist was an easy way to stop the script fully executing while I was just trying something out, e.g.

failure = driver.find_element_by_name("doesnotexist")

In my particular case, this quit the script before it got through to a full submission of the form, which kept the database a bit cleaner (and short-circuited the automatic sending of confirmation emails that kept arriving when the form went through).

I'm sure there are better ways to do some of these things, hopefully I'll improve as I go along. I'll follow up with more posts on this as a progress.

Comments