In my experience, UI tests are always very time consuming. Every sprint a whole host of UI tests break after developers make changes needed for new features or bug fixes.
Don’t get me wrong, I’m a big fan of automated testing! But for my latest projects I’ve stayed a way from UI automated testing completely. They are way too time consuming to maintain, let a lone extend.
Below I want to propose why UI testing is a nightmare to maintain and a hypothetical solution.
Stop Changing, Start Extending
There’s a very important principle in software development that helps developers write code that isn’t brittle: the Open/Closed Principle. It states:
software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
This simply means that a chuck of code, once deployed and in the world, should not be changed. In order to change behavior of a chunk of code, one will need to either extend it (using object oriented concepts of inheritance/polymorphism) or replace it with a different chunk of code.
Testing With Open/Closed Development
The huge benefit of writing code in a open/closed way is that when one writes tests for a chunk of code, the tests won’t break later when the developer changes it’s behavior since the developer will never change the original code, but extended or replaced it. The tests for the original code then won’t break because the original code didn’t change. Pretty cool!
UI Development and Open/Closed Principle
With unit testing, if the developers are violating the Open/Closed principle, then one will be getting a lot of broken tests to maintain. This is exactly what is happening with UI development:
Developers change the UI’s behavior, violating the Open/Closed Principle, and causing UI Tests to break.
This is great, but how do we apply this principle to UI views? Let me go through how we applied this to Web API development.
Versioning Web APIs
One practice that we have used to follow the Open/Closed principle with Web API development is to version them. This allows me as a developer to extend or replace behavior without breaking integration tests.
Here’s an example Web API route for retrieving news articles from a system:
GET: /api/v1/articles
The initial JSON response that was deployed to production was like this:
[
{ name: "article name 1", author: "Author Name" },
{ name: "article name 2", author: "Author Name" }
]
There were a bunch of integration tests written to make sure that the Web API returned the correct data in the correct format. Later, the client wanted pagination. So we needed to change the format to this:
{
results: [
{ name: "article name 1", author: "Author Name" },
{ name: "article name 2", author: "Author Name" }
],
total: 32
}
Well, that would break all the tests. To follow the Open/Closed principle (extend, don’t change), we’re lead to create a new version of the Web API:
GET: /api/v2/articles
The tests for the original Web API didn’t break and we could create new integration tests for the new version. Awesome!!! No broken tests and new functionality! And no acid reflux as well!
Now let’s try to apply this concept to UI view development.
Versioning UI Views
Let’s say we have a webpage that displays articles in a list view:
/articles
To help with following the Open/Closed principle with UI development, this path will need to be configured through the routing framework to go to the first version of the webpage:
/v1/articles
The tests should then be written against this webpage. Thus the tests are written against a specific version of the webpage.
Now let’s say that the client wants to change the view behavior from a list view to a grid view, thus breaking all the tests that were written for the list view version of the webpage. No problem, create a new version of the page!
/v2/articles
New UI Tests can then be written for the new version of the webpage and the old tests will continue to work. Configure the routing framework to use the new version in the site. Kaboom!!! No broken tests to maintain!
The only problem to solve is when to retire old UI views and their tests. But that one can do on their own time schedule, instead of racing to fit within a development cycle.
Make It So.
Maintaining broken tests adds so much stress and consumes so much time. Hopefully this discussion helps shed a new light on UI testing and provides a step forward for you and your team in creating maintainable and extensible user interfaces.