Knowing how to create flexible locators is critical to crafting stable tests that will hold up in the long run. The example I use in Telerik’s Online Instructor Led and Team training sessions centers around working with a row from a table. This table, to be precise!


Note the grid control has created an ID value for that row – and that ID is completely position-based: ctl00_MainContent_PeopleGrid_ctl00__1. (Look at the row above and below. The IDs all end with “__<number>”.)

By default, Test Studio’s recorder will define this row’s locator using that ID value. Normally IDs are the preferred route to use since your test script will always find that element regardless of where it is on the page.

However, if you’re writing a test that’s specific to Jayne Cobb, such as a Retrieve or Update test, then you don’t want your test failing if the sort order changes. In this case, using that ID value increases the brittleness of this test since the test will fail if another record is added before Jayne Cobb’s, or if the table’s sort order changes. We may want tests verifying sort order; however, those are separate tests.

You can make this test completely independent of the row’s position in the table by using a chained Find expression. The idea is to start your Find expression at the table level, then narrow down to an extraction based using a table row and the applicable inner text.

All this is easy to do via the Edit Element dialog. (You’ll need to have the row added in to the Element Repository already.) Right-click on the element in the repository and select Edit Element (The image below is from Test Studio Express in Visual Studio. It’s the same process in Test Studio Standalone.)


You’ll want a live connection to the page so you can validate your Find expression.


Use whichever connection method works best for you – the Current Page option is really handy if you’ve got an IE instance open on the page already!


The great thing about the Live Connection is you can see the highlighted area on the live IE instance as you’re building your chained expression. For this example I start with the outer DIV element holding the grid. As you can see below, I’m using an explicit tagname filter plus the ID of the element. I’ve also clicked the Validate button which checks my find expression’s validity against the current DOM and highlights the portion of the page that’s selected via the Find expression.


With tables/grids I prefer to ensure the focus of the Find expression is limited to the table body, so I’ll add in another filter group with tagname=tbody. Finally I’m ready to add the filter group for the table row. This one’s easy too! I want any row with Jayne Cobb in it, so I’ll use tagname=tr and innerText contains ”Cobb”. Note that is contains.


There you have it! Flexible find logic that won’t break when you change sort order.

You can take this same exact approach and extend it to columns in a row you’re working with. For example, the Edit cell is something we want to interact with regardless of which column it’s in. Follow the same steps as above and add on another filter group to get the edit cell dynamically.


You can do exactly the same things as this in coded steps if you needed to. This snippet grabs the grid directly from the page via its ID value, then pulls the row with Jayne Cobb out. Finally, we’re getting the Edit anchor and clicking it.

RadGrid grid = Find.ById<RadGrid>("ctl00_MainContent_PeopleGrid");
HtmlTableRow jayne = grid.Find.TableRow("Cobb");
HtmlAnchor edit = jayne.Find.ByContent<HtmlAnchor>("Edit");

Need to validate a business logic query is returning the right records from your baseline dataset? The following snippet will dynamically query the grid to find all elements which contain “New Earth”. Note we’re grabbing the grid directly from the Element Repository. The Pages object exposes all the pages and elements in your project as strongly-typed objects for you to interact with!

RadGrid grid = Pages.DemoPage.Content_Grid;
IList<Element> newEarthContacts =
   grid.Find.AllByContent("New Earth");     
Assert.AreEqual(2, newEarthContacts.Count);

The concepts laid out in this post will help you write locators that are much more flexible, ensuring your tests don’t needlessly break when your UI changes. (Although you may have other specific tests which should break in those circumstances!)

Earlier this week I posted a new video to Telerik TV walking through all this. You may find that useful too!


Comments are disabled in preview mode.