You’re reading the second to last post in a series that’s intended to get you and your teams started on the path to success with your UI test automation projects:
1. Introduction
2. Before You Start
3. People
4. Resources
5. Look before You Jump
6. Automation in the Real World (you are here)
7. Improving Your Success
Important note: After its completion, this series will be gathered up, updated/polished and published as an eBook. We’ll also have a follow-on webinar to continue the discussion. Interested?
So here you are: ready and raring to get real work done. Hopefully, at this point, you're feeling excited about what you've accomplished so far. Your team has set itself up for success through the right amount of planning, learning and prototyping.
Now it's time to execute on what you've laid out. Remember: your best chance for success is focusing on early conversations to eliminate rework or
In my last post, I tried hard to really hit the importance of collaboration and early communication as fundamental pieces of a successful project. Now it's time to focus on the actual mechanics of the work.
The next few sections dive
Although discussion around pair programming has always focused on developers, pairing is a wonderful tool that can be used by all members of a team.1
Developers and testers should work together on UI automation for a number of reasons, including:
Far too many people think pairing means working side-by-side for eight hours a day. NO! Pairing doesn't have to be high-stress, full-time effort. Spend as little time together as needed to solve a particular problem, or spend as much time together as the members feel comfortable with.
Also, pairing shouldn't be dismissed because teams aren't
Pairing at its root is knowledge sharing. Investing to encourage and expect the team to pair up reaps rewards over the long run.
Perhaps the most crucial concept to get straight at the start is the need to treat your test code like production code—because test code IS production code!
Delivery teams absolutely have to invest time in creating maintainable system code through well-established conventions like low complexity, readability, carefully thought dependencies, etc.
UI test automation code needs to be treated exactly the same way. You need to take the same approaches to simplicity, clarity and so on, regardless if you're writing browser tests in WebDriver or creating them in Telerik® Test Studio®.
Teams that don't approach their test suites (or system code) this way are doomed to suffer lost time due to brittle suites. Those teams are also going to pay a heavy price when trying to fix brittle tests, due to their complexity. It's easier to be successful when you start out right by giving your test suites some love and maintainability.
Three great principles I've made use of over the years have helped me keep my UI tests as maintainable as possible.
Abstraction is also helpful because it hides the implementation details from the calling test or method. With the
You'll save your team tremendous amounts of time, frustration and grief if you focus on maintainability at the start of your UI test automation projects.
Backing APIs, sometimes referred to as test infrastructure, are abstraction tools. They're perfect examples of how testers and developers can collaborate to leverage each other's best skills.
I've found not many testers have deep programming backgrounds, which is absolutely fine. Few testers understand how to create authentication headers to successfully call web service endpoints. Nor do many testers understand how to create secure, performant, reusable connection pools to a database.
That's all fine, because testers' skills lie in testing. Developers, on the other hand, generally do those tasks on a frequent basis.
Working together, teams can build an abstraction layer of a backing API that enables testers to very easily call methods for setting up prerequisite data or turning off features to make the system more testable.
The great thing about abstraction is you don’t have to know (or care) how the API does its work. Are new users created via a web service, or direct insertion to the database? Don't know, don't care. Backing APIs let you abstract all that away so you don't worry.
Moreover, if the developers create better methods for accessing the system, say moving from a stored procedure call to a web service, the tests won't be affected at all.
That's all fine and dandy, but what practical things should you look to do with a backing API? Here are a few things I use in every project I've worked on:
Backing APIs don't have to be complex, and you should only build them out as you need them. Be very lean as you create them.
Far too many systems are built without testability in mind. Architecture and coding design decisions impact testing at both the system and UI layers. System-level testability requires specific architecture and design decisions. Testability at the UI layer can be a much simpler matter of adding in good element IDs where possible.
Some web technologies such as Ruby on Rails add ID attributes to elements by convention. Nearly every web stack from Rails to ASP.NET WebForms makes adding IDs to regular elements a snap.
Additionally, developers and testers working closely together can easily solve problems such as dynamically generated IDs that hinder testability—or don't render them at all.
For example, Telerik® Kendo UI® has a Grid control that doesn't include IDs by default:
It's easy to add a bit of JavaScript to the Grid's definition, to create useful IDs that include data unique to each record:
dataBound:
function
(dataBoundEvent) {
var
gridWidget = dataBoundEvent.sender;
var
dataSource = gridWidget.dataSource;
$.each(gridWidget.items(),
function
(index, item) {
//use next three lines for html ID attrib
// with custom ID of database ID + lname
var
uid = $(item).data(
"uid"
);
var
dataItem = dataSource.getByUid(uid);
$(item).attr(
"id"
, dataItem.Id +
"-"
+ dataItem.LName);
//Use this line to show html ID attrib with row #
//$(item).attr("id", index);
});
$(
".k-grid-add"
).attr(
"id"
,
"create_btn"
);
},
Now the Grid's records each have a unique ID composed of the identifier from the database, plus the last name of the person on the row.
This approach is obviously specific to this example; however, that's the beauty of this approach. Use your tools at hand to solve the specifics of the situation you're encountering. Maybe your IDs need part numbers, zip codes or something else. That's fine! Construct them as needed to get testable pieces in place.
A final piece about testable UI: You're not limited to just ID or other attributes. There are all sorts of things you can add to the UI to help testing. Think of flags you can add to handle complex asynchronous or queuing actions.
For example, the image below shows a new element being added to the page after a Create action completes. This gives you something additional to "latch" onto when an action is complete.
This particular example is again in Kendo UI as a new method in the control's DataSource definition:
requestEnd:
function
(e) {
var
node = document.getElementById(
'flags'
);
while
(node.firstChild) {
node.removeChild(node.firstChild);
}
var
type = e.type;
$(
'#flags'
).append(
'<div responseType=\''
+ type +
'\'/>'
);
},
While this example is specific to Kendo UI suite, again the underlying concept is the same regardless of the technology stack you're using.
All this discussion about modifying the UI to be more testable is wonderful, but what about when you're stuck with a UI that can't be changed? Maybe it's a legacy system that's got limited maintenance. Perhaps it's something built on top of a third-party system or platform, say SharePoint, Sitecore, or Orchard.
In those cases, you'll need to work hard to learn flexible approaches for building locators that work with the system/stack/platform you're using. In many cases, you'll find yourself having to fall back to locators based on convoluted XPath or JQuery selectors. Evaluate those locators as carefully as possible to ensure you're using the best locator possible.
Please note I specifically said "...the best locator possible." In many situations you won't be able to get a perfect selector. In those cases, you'll need to become adept at using combinations of things such as IDs, CSS classes, name and other attributes, plus some XPath to scope down to what you need.
InnerText remains one of my favorite locator strategies, because it enables you to find things such as table rows using data that should be in that row. It's also very handy when you're simply not able to find other usable locator strategies.
Success in software, regardless of whether you're writing multithreaded database transactions or user interface functional automated tests, is all about the long view. Of course you have to write tests that are solid, high-value and correct, but you absolutely have to keep your eye on how useful those tests will be over time, and how costly they'll be to maintain.
Work hard to keep your tests simple, concise and flexible. Make use of the suggestions we've laid out here.
Did we miss something you've found useful in your own "real world" life of UI test automation? Did you find these topics helpful? Let us know in the comments!
1Read more about pair programming on its page at the Extreme Programming site.
2Martin Fowler has a nice write up on the Page Object Pattern.
Jim is an Executive Consultant at Pillar Technology. He is also the owner of Guidepost Systems. He has been in various corners of the IT world since joining the US Air Force in 1982. He’s spent time in LAN/WAN and server management roles in addition to many years helping teams and customers deliver great systems. Jim has worked with organizations ranging from startups to Fortune 100 companies to improve their delivery processes and ship better value to their customers. When not at work you might find Jim in the kitchen with a glass of wine, playing Xbox, hiking with his family, or banished to the garage while trying to practice his guitar.