6 Reasons to Co-locate Your Application and Automation Code
A popular school of thought holds that application code and test automation code have separate intentions, and so should reside in separate code repositories. I’ve followed this maxim for my entire career. But on one of my latest projects, I decided to see how the other side lives and create a brand-new test automation project in the same repository as the application code that it tests.
To my surprise, I discovered six key advantages.
1. Shared resources: Identifiers and more
Unreliable web element locators are a common cause of flaky test automation. As developers change identifiers within an application, the test code gets out of sync and must be updated. When you create a new automation project, the first benefit is that you can take advantage of libraries that define those identifiers.
For example, developers have a class that holds the accessibility identifiers used on all elements throughout the application. So, instead of going through the painful task of digging through the Document Object Model (DOM) to construct reliable identifiers, only to have them inevitably break when the application changes, you simply use the same identifiers that the developers use. As you make updates to those identifiers, the automation code remains stable because it points to the same source.
This was such an epiphany for me that I dug around in the application code to see what else I could use to make my automation code more robust. One developer pointed out that the library of localized strings can also provide value. Yes, it certainly can!
In many tests, you use assertions to verify text. The vast majority of automation projects I’ve worked on were scoped to English-only because trying to target multiple languages would require more effort than management was willing to invest—for test code, that is. But for application code, localization is a must.
By living in the same repository, our test code can run against any of the languages we support, with minimal effort. The only thing we needed to do to achieve this was to avoid hard-coding text within the test code. Instead, we use the library of localized text that already exists in the shared repository.
2. Access to lower layers
Most practitioners agree that automating tests at the UI layer is costly. Because of this, I’m always looking for shortcuts within the application to test functionality at a layer lower than the UI.
Co-locating your automation code with your application code makes this task much easier. Not only am I able to use web services for faster, less brittle execution, but I’m able to call into business methods that may not have public-facing APIs.
It’s simply a matter of figuring out which function is being called from the UI. Then you can skip the UI and call the function directly. It also helps make your test code more reliable.
3. Earlier feedback
Most developers are good about running unit tests before checking in code. After all, no one wants to break the build. As more teams move toward continuous integration/deployment, though, they need more thorough test automation to gate the deploys.
When your test automation code lives in a different code repository, you typically check in and build the application code before executing the test automation. But when I had my automation code in the same repository, I saw developers execute the longer automated scenarios at the same time that they ran their unit tests—before check-in. This gave them much earlier feedback on the code that they wanted to submit and put our team a step closer to its continuous deployment goals.
4. Developers become more invested
When developers break unit tests before check-in, they typically evaluate the test to see if the failure is due to an intentional change. If it is, they update the unit test to reflect the new intention of the application. If it isn’t, they fix their code.
Either way, code should not be checked in until it’s working as intended and all unit tests have passed. When your automation code lives in a different repository, developers are less likely to make code fixes for the test automation when needed. But if these tests are located in the shared repository and are run as part of the earlier feedback loop, developers are more likely to help with maintenance.
Also, when automation engineers are checking code into the shared repository, our developers are more likely to participate in code reviews of the automation code. The dividing line between “their” code and “our” code becomes much thinner, and developers feel more invested.
5. Automation engineers become more involved
Just as the developers help more with the automation and application code are co-located, it’s also easier for automation engineers to help more with unit tests when they are comfortable with the shared repository.
While developers primarily carry the load of writing unit tests, automation engineers, who tend to be more skilled at testing, may have additional scenarios that they want to cover at the unit test layer. Instead of asking the developer to add them, automation engineers can simply add the unit tests themselves. This further blurs the line marking who owns test versus automation code, and instead reinforces a culture where quality is everyone’s responsibility.
6. Application code becomes more testable
As I became more comfortable adding unit tests, using shared resources, and calling into the business logic layer, I realized my favorite benefit of all: I no longer have to deal with an application that’s not testable.
In the past I’d have to beg management to free up developers to work on making the application more testable, and when that was unsuccessful (which was about 95% of the time), I’d have to jump through hoops in my automation code or abandon my test altogether.
But by having direct access to the application code, familiarity with the project, and a comfort level sufficient to make changes, automation engineers can implement the changes needed to make testing easier. Examples include everything from disabling random, intermittent popups (e.g., “Is your phone number still valid?”), which are absolute nightmares for automation; to including useful logging that helps test code verify more detailed and nuanced background events.
Strangely enough, making direct changes to the application code is also a sure way to guarantee developers’ participation in the code review. They can see the struggles you have with test automation, and learn ways to produce more testable code themselves.
Those were all of the wonderful benefits I missed by insisting that all of my automation projects reside in their own repositories. I’m glad I challenged that belief and opened myself up to new experiences, and you should try it too.