Apr 17, 2007 / Selenium Usage in Real Life ASP.NET 2.0 Application

4 comments

We are writing Selenium tests about 2 months at TargetProcess and at the moment we have created 34 tests with 190 assertions. It is not as much as we have NUnit tests (750), but coverage increases and we expect to have good coverage within half a year.

TargetProcess based on ASP.NET 2.0 and AJAX, quite hard combination for automated testing. In general we've looked into testing framework integrated into Visual Studio, but it does not support AJAX (support is possible, but demands too much effort). Then we've tried Selenium and it worked. In general it is very easy to create tests using Recorder, however there are some problems that seriously affect maintainability.

ASP.NET IDs Workaround

The first ad obvious problem is complex ASP.NET ids. As you know, ids are auto-generated, and you have something like ctl00_mainArea_bugDetails_BugDetails_btnUpdate in the code. When you record test you will have something like that:

<tr>
 <td>click</td>
 <td>ctl00_mainArea_bugDetails_BugDetails_btnUpdate</td>
 <td></td>
</tr>

What happens if you change mainArea id to something else? Right, all your tests will break. Global replace may solve the problem, but still it is error prone. We are using XPath to find required button and code looks like that:

<tr>
 <td>clickAndWait</td>
 <td>//input[contains(@value,'Save')]</td>
 <td></td>
</tr>

Locator //input[contains(@value,'Save')] will find element INPUT with value 'Save'. It is less now if you change mainArea id all tests still work.

That is the reason we write tests manually. Recorder is not as clever as we want it to be.

AJAX Support

Selenium supports AJAX without problems, but you should know one hint. Selenium does not know when AJAX action is completed, so if you need to wait till completion ClickAndWait will not work. Instead you should use pause. Expand All action is asynchronous and the following code will not work correctly

<tr>
 <td>clickAndWait</td>
 <td>//a[contains(@title, 'Expand All')]</td>
 <td></td>
</tr>
<tr>
 <td>verifyTextPresent</td>
 <td>Completed Story</td>
 <td></td>
</tr>
Correct code shown below. ClickAndWait action replaced with click and pause actions.
<tr>
 <td>click</td>
 <td>//a[contains(@title, 'Expand All')]</td>
 <td></td>
</tr>
<tr>
 <td>pause</td>
 <td>1000</td>
 <td></td>
</tr>
<tr>
 <td>verifyTextPresent</td>
 <td>Completed Story</td>
 <td></td>
</tr>
There is a risk that pause will be too short and request will not be received, so there is one more way with waitForCondition action.
<tr>
 <td>waitForCondition</td>
 <td>var value = selenium.getText("//div[@id='lstAction']"); value.match(/User Story saved/);
</td>
 <td></td>
</tr>

However we did not see problems with pause and it is simpler for us.

Custom Actions

We had one problem (it is quite common problem for web applications). In lists we have different actions (view, edit, delete) and in test you may need to execute the action for specific item. For example, delete specific user story from the list. It is hard to find required link with XPath since there are many looks-alike links. As a solution we've created clickInTable custom action.

<tr>
 <td>clickInTable</td>
 <td>//table[contains(@id, 'projectsListControl')]</td>
 <td>Test Finish Project|delete</td>
</tr>

ClickInTable action finds required table by id, then finds row by item name and finally finds required action. It took several hours to create such method, but it worth the effort.

In the last part of the Selenium posts I will share some thoughts about Selenium-TargetProcess integration.

4 Comments:

At 12:27 AM, Anonymous Anonymous said...

I love real world usage reports like this. Keep them coming. :-)

Your comment that "Recorder is not clever as we want it to be" is right on. People should never treat any record/playback tool as a "record, save, and forget it" tool. Always expect to rework a recorded test to make it more maintainable. (I've heard the complaint about .Net's dynamic id tags many times.) Regardless, though, we will try on the Selenium project to make the recorder more clever about waiting for Ajax events for you anyway. :-)

And on a side note, yes, I agree that using "pause" is simpler, but I would consider its use "technical debt" that needs to removed sooner than later. When used too much, the cumulative effect of many "pause" commands can really slow down total testing time. And it's amazing how soon total testing time becomes an issue on projects making heavy use of Selenium.

 
At 1:54 AM, Blogger Michael Dubakov said...

Thank you for the comment. I think you are right about pause usage. We will rework the tests :)

 
At 4:33 PM, Anonymous Anonymous said...

This use of Xpath in Selenium tests is extremely powerful. In Microsoft CRM, the div clicks arent handled by the recorder because they don't have IDs. This is a solution for that issue. Thanks.

 
At 4:37 PM, Anonymous Anonymous said...

Here is another great tool (a Firefox addin) to help with your custom Xpath queries.
http://slesinsky.org/brian/code/xpath_checker.html

 

Post a Comment

<< Home

Subscribe to the RSS feed
Stay tuned by having the latest updates via RSS
Follow TargetProcess on Twitter
Get in touch with our team

TargetProcess is an agile project management tool. It is designed to solve distributed teams problems and support agile development processes.



Key Features

  • Full Agile Project Management Support (Scrum, XP, Custom)
  • Productivity Tools (Tp.Tray, ToDo list, dashboards, Inline editing)
  • Customizable Development Process
  • Subversion Integration
  • Integrated Bug Tracking, Help Desk, Time Tracking

Get TargetProcess for Free (5 users pack)

Previous Posts



    follow me on Twitter