Thursday, March 20, 2008

Selenium IDE Test Tips


Here are a few things to bear in mind when creating Selenium IDE tests.

Setting and Getting Variables

store | 10 | x

This obviously sets x=10. There are a couple of ways to reference it: ${x} or storedVars['x']. They're not the same.

The first, ${x}, is from Prototype. Its a shortcut and returns a copy of storedVars['x'], it doesn't actually point to the real value. The second, storedVars['x'] directly references the array of Selenium's stored user variables. I recommend using storedVars['x'] and not ${x} (even though it's shorter).

You can't assign anything to${x}. Within a single Selenium command, if you modify storedVars['x'], the value of ${x} won't get updated until the next command. Stick with storedVars and you'll be ok.

When you want to use a previously stored variable as an argument to a subsequent command, use the "javascript{...}" notation. For example:

assertElementPresent | javascript{storedVars['myElementsXpath']}

XPATH from Firebug

You tend to use xpath queries a lot in Selenium tests. Sometimes figuring out the xpath expression for an element is a real pain. Firebug can help. Just click on the "inspect" tab and highlight the element you want. Right-click on it in the HTML code display and select "Copy Xpath" from the context menu. Paste it and give it a try. You may have to replace "/html/body/" with "//" though.

Xpath can get interesting. I like Zvon's tutorial.

Using storeEval

There are ways of defining functions even in Selenium IDE, but a simple approach is to use storeEval to run some Javascript code and put the result in a variable you can use for subsequent commands. For example, say you need to click on ButtonA if its present, or else click on ButtonB if its present. If neither one is present you want to do something else.

In this example I've got my xpath expressions for ButtonA and ButtonB stored as variables (just to illustrate the aforementioned javascript{} syntax). I'm going to fetch them and use them in my evaluation.

storeElementPresent javascript{storedVars['ButtonA_Xpath']} ButtonA
storeElementPresent javascript{storedVars['ButtonB_Xpath']} ButtonA
storeEval (storedVars['ButtonA']==true)?storedVars['ButtonA_Xpath'] :((storedVars['ButtonB']==true)?storedVars['ButtonB_Xpath']:null); linkToClick



That's one way to do it. Another is to use conditional logic and flow control to set up an if-else structure. Check out the Selenium IDE FlowControl post for info on how to do conditional logic-based flow control.

In the end, if you're testing a web application you've designed and your test case is getting this ugly, you'd better re-evaluate your design. If you're testing something other than a web app though, for example a test case that spans multiple sites, you might need to get creative.

14 comments:

Anonymous said...

Hopefully this isn't going to be a stupid question. But how does your "if" statement in your example for the storeEval command work ? When I try it, I always get the following IDE Error message: "[error] Threw an exception: return not in function"

The only work-around that I have ever been able to figure out is to basically declare a function that returns the value.

Anonymous said...

I'm trying to do something similar with a conditional check - it looks like storeEval | if (condition) { CaptureEntirePageScreenshot (file)}.

But I can't seem to access the selenium CaptureEntirePageScreenshot function from within the javascript if statement. I want to be able to use the same scripts for IE as FF, so I want to check the browser and do the capture if it's FF. I've tried putting selenium. and browserbot. and this. and combinations of these in front of the capture function but to no avail. Any ideas?

Darren said...

Use the flow control extension, either the original version that works for Selenium RC or the one I ported to Selenium IDE.

Anonymous said...

That's what I'm doing now, which allows me to do a conditional gotoIf. But that's requiring me to have three lines for every screen capture I might want to skip -
Line 1: gotoIf (condition) label
Line 2: screen capture
Line 3: label

I'd like to condense the process to doing a storeEval

storeEval (if condition) {screen capture }

But the problem is that when I execute storeEval the javascript for the if condition can't "see" the screen capture command anymore. I was wondering if you knew how to reference that?

Darren said...

You might be able to find away around the scoping limitations, but note: Selenese commands weren't meant to be used in this way. Rather than being a library of Javascript routines to be called from by your scripts, they're meant to be top-level directives that replace scripted statements. Alas, the "right" way to do this - and the supported mechanism for doing so - is the flowControl extension.

If your main motivation is simply to "condense" the testcase, you might want to consider what this does to readability and maintainability. Granted, its a couple more lines, but there's no question about the intent.

Anonymous said...

Understood. I think I'm going to have to go that route, as I was able to get by the limitation of accessing the function :
storeEval | if(browserVersion.isChrome) {Selenium.prototype.doCaptureEntirePageScreenshot("c:\\test.png") } but I now get the error:
[error] Threw an exception: this.browserbot is undefined

Thanks for your help and suggestions.

Elec_Monk said...

I've used the store and ${varname} references to duplicate similar tasks using the IDE (v1.0.1).
I'm finding that when I run the suite in TESTRUNNER.HTA (which I copied into the core directory cos it didn't come with 1.0.1), the ${varname} is being treated literally, instead of being interpreted. That is, it is entering "${varname}" into a field instead of "textstoredinvarname". It works fine when running in the IDE.
Is there a way to do this variable substitution that is testrunner friendly?

thanks,
Brett

Shaila Rahman said...

Hi Darren,I'm a new Selenium user. I have installed goto_sel_ide.js. I'm using 1.0.4 Selenium IDE.I implement the if/else statement with storeEval as you shown us. I'm always getting 'Threw an exception: return not in function' error. Do you have any suggestion for me? Here is my code:

open http://www.seelium_test.html
click Button1
click Button2
storeElementPresent //*[@id="Button1"] Var_buttonA
storeElementPresent //*[@id="Button2"]Var_buttonB

storeElementPresent javaScript{storedVars['But_A_XPath']} Var_buttonA
storeElementPresent javaScript{storedVars['But_B_XPath']} Var_buttonB

storeEval if (storedVars['Var_buttonA']==true) {return storedVars['But_A_XPath']} elase if (storedVars['Var_buttonB']==true){return storedVars['But_B_XPath']} else {return null} storeElementPresent

Darren said...

Hi Shaila,
Mea culpa - I wrote the example without properly testing it, and there was indeed a problem with the syntax. I've edited the example in the post to use the (somewhat obscure) "?:" notation. As you can see, this results in practically unreadable code, so I do recommend following the suggestion in this post to use the "if-else" strategy with the flow control extension instead. The point of the article was to explain how variables are accessed, but the right way to implement conditional logic is with the flow control extension using gotoif.

Hope this helps.

Brosig said...

Hi, great post.

I'm trying to store a value, but i need it to be case sensitive... You know a way to help me?

thanks.

Chandru said...

Excellent Info for selenium

Jinil said...

Hi Darren,

How can I go to a step if any exception occurs or page load fails?

For instance, If 1st command is to load google and it fails due to some issues, I want to redo this command, ie, try to open google again. Whats the condition to be used for gotoif.

Thanks :)

Anonymous said...

Thanks, excellent tutorial. It got me to figure out how to create a variable that was one more than another:

storeEval | parseFloat(storedVars['site_count']) + parseFloat(1) | next_site_count

N.B. I had to force the floats otherwise the javascript does a concatenation

Anonymous said...

Thanks, excellent tutorial. It got me to figure out how to create a variable that was one more than another:

storeEval | parseFloat(storedVars['site_count']) + parseFloat(1) | next_site_count

N.B. I had to force the floats otherwise the javascript does a concatenation

Productivity and Note-taking

I told a friend of mine that I wasn't really happy with the amount of time that gets taken up by Slack and "communication and sched...