As soon as you mention an automation framework, one would find people start talking about Selenium, TestNG so on and so forth. I classify the automation project’s architecture designed by the automation team as a framework, the underlying tools could be any unit test framework or automation tool used.

Most automation efforts get stuck in the maintenance phase where the upkeep of the scripts starts to outrun the utility we are getting from them. The architecture is not designed with best practices in mind to coup with upkeep and future needs of the product.

While working on safety critical products I caught up on a few design principles heavily used in that industry. Here are the design principles I call ‘The pillars of framework design’ learned over the years.


The only constant is change. If the code we write today is going to be hard to maintain in the future, the rework and upkeep cost is going to skyrocket. Nicely written code in my books does not just do the job today, but can keep up with the changes to come in time and be easy to maintain. I recommend every team to have a definition of maintainable code they want to follow as per their needs.

There are a lot of factors determining if a piece of code is maintainability. I would elude to a few here, a detailed discussion on these would be done some other time.

  • Naming conventions should be defined and followed.
  • Code commenting standards outlined and should be able to create code documentation from it.
  • Code complexity should be to a minimum. Automation projects do get complex anyway if there is no check on this, they can get out of hand pretty quickly.
  • Logging of test results. Report logs should not just show what passed and failed, rather should have debug information in there (we know we’ll have false negatives right!) and be an easy read at the same time.


The way automation generates great benefits is by being reusable, so should your individual modules in the code. Reusability should be embedded in everything you do in an automation project.

A major concept here is creating wrappers. Again, from my old embedded lessons (the buzz word today being IoT), for areas which we expect fundamental changes to happen, it’s best not to use them directly, instead create a wrapper on top of them even if it would have just one call in it. This makes enhancements easy and adds lots of portability.

Here are a few places to keep reusability in mind:

  • Selection of scenarios to automate.
  • Creating architecture of the project, have separate layers calling / utilizing each other.
  • Methods within each layer could be used by one another.
  • Reusable test data.
  • Smaller tests which could be combined to create larger / use case checking scripts.


Often when teams stat with automation they have the immediate goal in mind and the long term picture is taken into account. In most automation projects, there will always be a need to expand the project adding more tests and functionality to it. Therefore, the architecture should be built keeping in mind future scalability. Time and again have I seen projects not able to scale up and the need for a massive rework.

On starting an automation project, I would estimate of how many test cases we can expect to run, for instance one project was 5000 scripts. One the initial framework design is completed, we ran a sample project running 5000 scripts (a few tests can be executed again and again) to see if everything would work on scale. This would not be exactly like the actual case, but would be a good estimate.


The most important pillar for an automation project. The environment in which these scripts are to run is ever changing. The AUT (Application Under Test) is changing, browsers / services are changing even the automation tools / languages are changing. With all this change going on, it’s no surprise automation projects do succumb to robustness issues.

This is a vast subject and lots of variables to consider here, however I’ll be eluding to just a few here.

The root cause of flakiness (the automation term for not robust) is when the expected state of the AUT is not matching what the automation code is expecting. Therefore, the main guideline is to identify the possible states the overall system can go into and handle them in the code.

I break this exercise down into two areas, proactive and reactive handlers. Proactive handlers are where we know there is a higher possibility of something going wrong and we prevent it before it can happen. A great example would be delays. We know for a modern web application, delays are going to be a problem, use dynamic delays as a standard rule before every action. Test data would be another one. If you are sure there will be a problem (which most of the time there is), handle it can turn into a problem. (refer this article in TEST Magazine to read more).

Reactive handlers are when the state change has happened, and now we want to get back on track, let’s say an unexpected popup appeared. Under this heading most the important tool is event handlers. Apart from the basic Try{} Catch{} blocks, have event handlers to take care of unexpected events like unwanted popups, a page not loading and so on.

While this post does not prove to be a complete guideline, it’s a food for thought on important aspects of framework design.