When automating tests, avoiding creating brittle tests are very important. In my experience, this something that can easily happen when using Selenium if you don’t take special care.

For instance, when locating elements on the page, you typically would like to refer to them using IDs. However, can you be sure that the IDs stay the same at all times? No. This problem becomes appearent when creating ASP.NET applications in Visual Studio. Visual Studio automatically generates what is called the Client ID for a control, based on its location in the control hierarchy. This id is rendered as both the id attribute and name attribute of an HTML element. For example, an ID might look like this:

ctl00_MainArea_MainPageControl_MainPageSection2_ClearRepeater_ctl01_HyperLink1

As a consequence, if you move a control to another place in the control hierarchy, the ID will be different. If you have referred to this ID in your Selenium test, it will break! Not good.

In order to fix this situation, I created a locator that does a partial match on the element id attribute.

Share
  • http://www.connectionsacademy.com Lyle

    I added your partialId user extension to my user-extensions.js file. I have had success with most fields, but failure with others. For example, if you visit the page http://www.connectionsacademy.com/home.aspx and click on the link at the top left page labeled “Request Information” you get a RadWindow Frame pop-up. I select the frame and then start entering data in the form using a script I created with Selenium IDE and FireFox 3.5.4. The first name, last name, address and city fields all enter their data using the type command similar to the following:

    ” type | partialId=firstName | bobFirstName “

    However, the phone number field fails to enter the information into the field, but there are no errors in the IDE log and it continues to the next step in the test. Pasted below is the log from executing the script:

    * [info] Executing: |open | /home.aspx | |
    * [info] Executing: |click | partialId=requestInfoButton | |
    * [info] Executing: |selectFrame | relative=up | |
    * [info] Executing: |type | partialId=firstName | First Name Field |
    * [info] Executing: |type | partialId=lastName | Last Name Field |
    * [info] Executing: |type | partialId=addressLine1 | Address Line 1Field |
    * [info] Executing: |type | partialId=addressLine2 | Address Line 2 Field |
    * [info] Executing: |type | partialId=city | City Field |
    * [info] Executing: |select | ctl00_ctl00_ctl00_blankPageContent_masterPageContent_pageContent__control_customforms_requestinfo_ascx1_state | label=MD |
    * [info] Executing: |type | partialId=zipCode | 21202 |
    * [info] Executing: |type | partialId=email | lalbright@connectionsacademy.com |
    * [info] Executing: |type | partialId=phone | 410-555-5555 |
    * [info] Executing: |click | ctl00_ctl00_ctl00_blankPageContent_masterPageContent_pageContent__control_customforms_requestinfo_ascx1_cbGradesGoingIntoK | |
    * [info] Executing: |select | ctl00_ctl00_ctl00_blankPageContent_masterPageContent_pageContent__control_customforms_requestinfo_ascx1_childrenCurrentlyAttend | label=Regular Public School |
    * [info] Executing: |click | ctl00_ctl00_ctl00_blankPageContent_masterPageContent_pageContent__control_customforms_requestinfo_ascx1_submit | |
    * [info] Executing: |selectFrame | masterPageRadWindow | |
    * [info] Executing: |waitForTextPresent | We will send you information shortly on our public school at home program. | | 
    

    What’s going wrong here?

    Also, can the partialId command be used with the Select command … because if the answer is yes, I am having issues with that as well. Thanks for any help you can provide. I really appreciate the user extension. Nice work. :)

  • http://www.kongsli.net vidarkongsli

    Hi Lyle,

    This is quite a conundrum, but I have an idea what might be the issue. I think that you have more than one element on your page that contains ‘phone’, and that Selenium matches the first one it finds, which happen not to be the input field. (In addition to the input element itself, there is a span element which Id contains ‘phone’).

    If this is the case, there are two possible solutions:

    1) Use a partial Id which is unique, for instance ‘aspx1_phone’ to make sure that the input field is mached

    2) Try to change the partialId extension to use the XPath expression ends-with(s1, s2) instead of contains(s1, s2). If this works, there is only one Id on your page that matches ‘phone’. Then, the extension would be: PageBot.prototype.locateElementByPartialId = function(text, inDocument) { return this.locateElementByXPath("//*[ends-with(./@id, 'Z')][1]".replace(/Z/,text), inDocument); };

    Cheers, Vidar