Skip to main content

Applying the SACRED Model to Build Reliable Automated Tests

There are many things to consider when designing and creating an automated check, and it's often hard to remember all of them. This was a challenge I faced, and to solve it, I created the  mnemonic SACRED. Fun fact about SACRED before we dive into it, the mnemonic was originally SCARED, but I felt that was less appealing to folk getting started with automation or those looking to expand their skills! So I entered the letters into a scrabble solver and it returned SACRED, and it immediately felt right. I love the idea of teams treating their automated tests like sacred possessions that are invaluable to the team.

SACRED is a blueprint for automated tests, it can be used to evaluate existing tests and plan new ones. Utilising it as a blueprint is a great way to bring different roles and skills together. A stronger testing mindset could design the test, but a stronger coded/tool user could implement the test. The mnemonic allows you to capture all the critical aspects of the test. Or it could be used by a solo engineer to ensure they don’t forget critical aspects of an automated test.

SACRED is not completely unique, but it uses the language I prefer when it comes to talking about automation, and includes other factors I believe are crucial in designing an automated test that other mnemonics miss such as Arrange Act Assert (AAA) and Given When Then.

What Is SACRED?

SACRED is a heuristic, in the form of a mnemonic to assist with designing and implementing an automated test. It stands for:

  • State
  • Actions
  • Checks
  • Reporting
  • Execution
  • Deterministic

For me these are the building blocks and key principles for designing and implementing an automated check that is going to be valuable to a delivery team. If we overlook any of these aspects the value of tests diminishes. They could be less targeted, flaky or too slow to provide significant value to the team.

SACRED helps you anticipate and prevent that.

S – State

Majority of automated tests rely on the system having a number of prerequisites in place, I call this state. The system needs to look a certain way before this test can successfully execute. State could include things like:

  • Data -  Users, products etc.
  • Configuration - Language, URLs, properties
  • Feature flags
  • The fact the site/APIs are running 

In an ideal context we design tests to be atomic, meaning that they take care of themselves, and don’t rely on existing data, or the running of a previous test. However, in some contexts it’s not possible to be fully atomic, and that’s OK, because if we are aware we can factor that into the design of our automated tests.

What we want to avoid is having to run tests in a specific order, and having the next test dependent on the result of the previous test. This limits us when it comes to execution, and often makes it harder to debug when the tests fail.

Here are some questions to ask, when designing the state for your automated test:

  • What data needs to exist for this test to execute?
  • Do we need any specific settings/config to be enabled to run this test?
  • Do we need any specific settings for endpoints/integrations?
  • What data do we need to create for this test to execute?

A – Actions

Actions are the steps you need to take to trigger the behaviour you are trying to verify. The test steps if you’re even have to write a test case, just here those steps are code or tool instructions. It’s imperative here that we provide the test tool with all the instructions it needs to complete the test, which means we really need to observe how we interact with the system and translate that into the tool. If we miss any steps out, such as a wait, or we don’t realise we were doing something in a specific order the test will make the same mistake. These are the exact mistakes that lead to brittleness.

If this were a UI test, actions would be things like clicking, waiting and populating text boxes. In the API world it would be building an API request with the right headers and data. In the unit test world, it would be a call to a method or to create an object.

Here are some questions to ask, when identifying the actions you need the test to take:

  • What are the steps I need this test to take, and in what order?
  • Does the test have to make any decisions, if so what are they based on?
  • Do I fully understand the behaviour and am I 100% sure these are all the required steps

C – Codified Oracles

An oracle is how we decide if there is a problem here, and it’s codified because we’ve translated our human oracle, or try to, into a fixed hardcoded version. We commonly do this by utilising assertions. When you write an assertion, you are codifying your expectation of how you’ve seen the system behave on that day, this is your codified oracle. If the test detects any deviation from those expectations it will fail the test.

I prefer to talk about codified oracles instead of assertions, because sometimes to satisfy an oracle, we need to create multiple assertions. Assertions are the implementation of the oracle, but the real skill, and a very underrated one, is identifying the oracle in the first place. As mentioned in my earlier paradox article, it’s one of the many skills we can transfer from manual testing. Some engineers believe you should never have multiple assertions, I don’t believe this, I believe you should utilise the framework until you’ve fulfilled the oracles, and sometimes that means multiple assertions per test.

Problems may arise when your oracle is vague, brittle, or incorrect. A bad oracle may cause false positives, false negatives, or flakiness. For instance, a very common assertion is to check that there is text on the screen, but that assertion doesn’t check the size of the text, the colour, or the font. Maybe you don’t care about those things, or maybe you do and failed to implement your oracle. If you understand the technical side of your application you are hopefully implementing tests on multiple layers, when doing this it’s usually much easier to identify your oracles as the tests are smaller.

Here are some questions to ask, when identifying the actions you need the test to take:

  • What exactly are you trying to test?
  • How do you know the page looks OK? Be sure to codify all that.
  • Do the tools you are using allow you to fully codify your oracle?

Beyond more established heuristics

As mentioned earlier the established design patterns such as Arrange Act and Assert (AAA), stop here. They focus on the creation of the test, however, I’ve expanded SACRED to include executing and running the tests, as well as verifying you’ve created a good test. How can we ensure we design our tests to help us get the most value from them.

R – Reporting

Reporting takes on two meanings in SACRED. The first is reporting the results of executions. And the second is having the test report things to us, especially when they fail.

There isn’t a universally agreed way of collating test results. Some teams hook into a test case management tool, others might store the results in a database to aggregate over time, and some teams just leave the results on the pipeline. It’s completely contextual, but something you need to include in your tests and test framework. It could be as simple as adding an annotation ID, but if you fail to do it, your coverage and analytics could be off.

The second part of reporting I call: ‘Giving your robot a voice’. Our tests will fail, they’ll detect change, we want them to, but when they do we want them to help us as much as possible. This help could take the form of screenshots, videos, test progress, API response dumps, console logs and even application logs. Because we all know what we do when a test fails, we download the branch/latest, find the test and click run and stare at the screen watching the test executing hoping we can spot the issue. But we don’t need to do that, we know it failed, so instead get it to collate as many useful pieces of information as possible, which will speed up the investigation process. This will hugely reduce the time it takes for your team to get that valuable feedback automated tests provide, and the sooner you get that pipeline healthy again the better.

Here are some questions to ask, when thinking about reporting:

  • Is the name of your test clear and its intent obvious?
  • If this test failed, what information would I need to debug it?
  • Does this test need to map to a test case or story?
  • Have I included any useful comments in this test to help up future debuggers?

E – Execution

Execution is in the mnemonic to help us think about how we are going to execute our tests, and where we are going to execute them. You may be in a context that requires the same tests to run on numerous environments. This can often require changes to the test to handle any differences between the environment such as data, slower performance or different configuration.

Then we have to think about how our test executions are being triggered. Perhaps they are running on a pipeline, if so do we need to make any changes to the pipeline to get our tests to run. Or maybe you are in a greenfield project and there isn’t a pipeline, maybe you need to set that and therefore include that in the task.

We need our tests to be running, there is no value in having tens or hundreds of tests that are not being executed, it’s wasteful.

Questions to ask when thinking about execution:

  • What environments does this test need to run on?
  • Do I need to alter the pipeline to get this test to execute?
  • Do I need to update any documentation to help others run these tests?

D – Deterministic

This is what makes automated tests unique, and what gives them the edge over humans for some specific tasks within software testing. Automated tests should be deterministic, they should repeat the same steps identically over and over again, never getting distracted, never deviating from the instructions.

So, how do we confirm our automated tests are indeed deterministic? We test them. Yep, you read that right, we have to test our tests.

  • Here are some ways to test your tests:
  • Run the test at least three times
  • Reverse your assertion(s) and ensure you test can fail
  • Run the test on a clean environment

SACRED in Practice

In practice SACRED is something I do in my head, I barely write it down. I created it to help teach others how to design better automated tests. However, the best way I’ve seen people do this is by turning SACRED into a template/canvas and creating a blueprint for the test they are trying to implement. This could be within a JIRA ticket, a miro board, or even on pen and piper.

SACRED is an mnemonic, which is a type of heuristic. Heuristics are fallible, they are a rule of thumb, they don’t always work, or include everything. That said, I’ve seen SACRED be incredibly effective within teams trying to improve their automation efforts. The act of slowing down to think about these characteristics reduces the errors that creep into our automated tests, and in turn helps us trust our tests, and the valuable feedback they provide.

It’s also proven a great mechanism for bridging the gaps between roles within the team. Maybe you’re not a coder, but can design a rock solid test. You can utilise SACRED to document all the key information, and have someone else implement your test for you, knowing that you’ve provided all the key information by following the mnemonic.

It can also be used as a tool to evaluate existing tests. When exploring an existing test, map the code to SACRED to help you identify what the test is doing, but also any gaps in its implementation.

Closing Thoughts

If your tests aren’t viewed as a valuable asset to your team, or are repeatedly flaky and creating false negatives or false positives, maybe it’s time to slow down and analyse them against SACRED. Your tests should be invaluable to your team, allowing you to move quickly with comfort. They should be inviolable, they are sacred.

About Automated Testing series

You're at the fifth part of our Automated Testing series - a collaboration with Richard Bradshaw. The series will explore many facets of Testing Automation and its implementations.

  • In the first part, we explored the Manual and Automated Testing paradox. Check it out here.
  • In the second part, we explored the different types of UI Automated Testing. Check it out here.
  • In the third part, we looked at the essential types of Automated API Testing. Check it out here.
  • In the fourth part, we explore how knowing your system’s architecture, data flow, and integrations is key to building test automation that truly delivers. Check it out here.

The next part, coming out on 15 July 2025, will talk about how AI fits in the future of software testing. Stay tuned!

Comments