Tuesday 17 January 2012

Refamiliarising oneself with Selenium

My main focus over the last few years has been Information Security and more specifically Application Security.  As such I've not cut much production code of late and have probably let much of my development practices atrophy.  However, recently I've had the opportunity to get back on the development bike and ride around a little.

I'm used to, as of course we all are, writing unit tests at the service layer, the persistence layer and of course the presentation layer, so nothing new there.

As an aside, I remember many years ago having a conversation with a front-end 'Architect' that stated you cannot unit test a front-end; cue HTMLUnit/HTTPUnit/JWebUnit and some red faces.  (I wonder how those frameworks have progressed in the last few years - perhaps the topic for a future post).

Anyway, I've been kicking around some front end code whilst picking up jQuery and what not, so I thought I'd wheel out some of the old favourite *Unit frameworks and I thought I'd also reacquaint myself with Fitnesse and Selenium skills.  But one step at a time.  I thought I'd get back into Selenium slowly and rather than jumping straight into the client/server setup, I went with the Selenium IDE - the Firefox plugin.  This is a post recording said reacquaintance process over the last day or so.

Getting Started
Download http://seleniumhq.org/download/
  Selenium IDE 1.5.0
  I'm running Firefox 9.0.1

Once you restart Firefox you'll want to bring up the Selenium UI.
  1. Firefox -> Web Developer -> Selenium IDE.
I have created a small 'admin app' for the purposes of this post - slightly contrived - that allows me to CRUD an entity.  Its mainly basic HTML with some jQuery, and so that I can include more of the Selenium commands I've included other elements such as some frames; I have a navigation frame at the top of the page and a main frame below that which essentially acts as a content container.

So to the first test suite
The navigation frame is a good place to start.  To keep this as simple as possible I'm going to create a test case for each of the items in the navigation frame and verify the result by testing for the existence of some text in the content frame.


                                               "Home", "Search", "Create", "List", "Report"

Let the testing commence
  1. Bring up the context in the left panel and select New Test Case.  
  2. Rename the test by selecting properties. 
  3. Hit record - the red circle up on the right hand side Selenium. 
  4. On the browser, select Home. 
  5. Wait for the frame to display, select some unique text to that page. 
  6. Bring up the context menu and select 'verifyTextPresent your_unique_text' 
  7. Run the test case.  The bar is red...

Oh dear.  What happened there?   The error is:
[error] There was an unexpected Alert! [Error! Status = 0 Message = error Message = ]

So from that, you can deduce the cause; its simple.  Well, it is and its not depending on your Selenium experience.   You'll have to change the logging level to figure out more information on the problem.  I set this at 'Debug', it's a fair bit more verbose, but its worth doing it to begin with.  When you select this level it becomes much clearer.  Selenium is failing when its trying to select the content frame.  Okay, so lets clickAndWait; tell Selenium to wait for the page that we just requested to finish loading.  Let try running that test again.  The bar is red....


Grrrr.  What happened there?  The error is:
[error] There was an unexpected Alert! [Error! Status = 0 Message = error Message = ]

Same error as before.  I (thought I'd) found two distinct solutions for this.  The first is just to slow the speed at which the testsuite executes down.  You can do this by going to the slider and moving it slightly to the right.  This however does not address the root cause.


The second is, and I should have used this before trying clickAndWait tbh, to waitForFrameToLoad, or so I thought.  I added this after the clickAndWait (I could just revert this clickAndWait to just click), adding the name of the frame and a timeout, but I started to get the same error as above when I ran the tests at full pelt.  In the end I lost my patience, and as a rule of thumb I run the test at ~20% slower than full speed.  ISSUE_1 If someone know the solution to the root cause, please ping me; I'd rather not waste any more time on that particular issue.

So back to the tests
  1. Repeat the creation of test cases for each of the navigation elements.  Now you have five simple tests to run.  If the bar is green... and it is.  Sweet.
Now to save your work so far
  1. File -> Save Test Suite As... 
  2. Select a name and save. 
Now, let's simulate a cold start
  1. Firefox -> Web Developer -> Selenium IDE 
  2. File -> Open Test Suite...
Oh oh!


Ouch!  So what is going wrong here?  A quick look at the file that was written to disk only raises further questions. The file size for a start is only 1k.   On opening the file I'm presented with some html with only the test case names defined, no content.  Pants.  But, I was saving the testsuite after every change.  It turns out I wasn't saving the individual testcases.  I'll get my coat...  (This is so embarrassing I almost omitted it for this post)  Ah well, lesson learned, lets start again.  It is a bit annoying that you have to save each case individually, surely by saving the testsuite you're implicitly saying '...and save the testcases'.

I'm going to repeat the above, but this time save each individual testcase as I create it.  I've ended up with the following files:

Running the testsuite produces the following results; Runs: 5, Failures 0.  The bar is green...  Happy days.

Knock it up a notch

So these tests were really rather trivial, (barring the issue with waiting for a page to load...) so next we should tackle a form.
  1. Bring up the context menu and create a New Test Case.  
  2. Rename the test by selecting properties. 
  3. Hit record. 
  4. Hit Create on the navigation frame. 
  5. Fill in the form fields one by one.
  6. And stop recording.. 
Hey wait a minute.  Some of the form-filling didn't get recorded.  FFS!  ISSUE_2  After spending a bit of time searching the interweb, I found nothing to help me.  Grrr!  This didn't seem to make any sense, the form  tag was pretty standard, as were the input tags.

Well, it seems I was the architect of my own downfall; whilst trying to be a bit clever and have the input field label in the form field itself I'd inadvertently hit a Selenium edge case.  The issue was with the "placeholder" attribute of the input tag, after removing it from the input tag the actions were recorded as expected.  That was annoying.

    <input id="description" type="text" name="description" placeholder="some description" />

I could be lazy and remove all the placeholder attributes and record myself entering values into the form, but I'm going to use this as an opportunity to exercise my xpath skills.

Manual form filling

Doing this manually isn't that bad, plus I get some practice with xpath and with the Selenium IDE.  So for the first field of the form I want to emulate the user typing a unique identifier.

                   Basically this is saying, for the input field with the id attribute value of id, type in 667.

All of the fields follow the same pattern now all that needs to be done is to click the form submit button and verify that certain text is present.  Continuing in that vein I built up a sizeable set of tests always running the suite after each additional testcase.

After a while, working out the xpaths for elements became tedious, even for the more complex paths, so I ended up using FireBug (https://addons.mozilla.org/en-US/firefox/addon/firebug/)

Some tips
  1. Remember to select the correct frame
  2. Don't be clever and use the placeholder attribute
  3. Use a plugin that will help you with the xpaths
  4. Don't run the testsuite at maximum speed

4 comments:

  1. How do you get Selenium to recognize a modal window?

    ReplyDelete
    Replies
    1. Using WebDriver (aka RC) or the IDE?

      Delete
    2. Sorry Anonymous, I've been on leave.

      If you're talking about JavaScript Alert/Messages, then why not use waitForAlert(pattern)

      Getting an alert has the same effect as manually clicking OK. If an alert is generated but you do not consume it with getAlert, the next Selenium action will fail.

      Under Selenium, JavaScript alerts will NOT pop up a visible alert dialog.

      Delete