Start with acceptance tests and finish with (almost) no bugs

Michael Angelov
INLOOPX

--

Whether you are starting a new project or developing a new feature on an existing project, try to start by writing the acceptance tests first.

Acceptance tests, as compared to unit tests, possess no information about technical implementation. They are written in a human-readable form of scenarios, each consisting of high-level steps describing preconditions, subsequent interactions, and final expectations of the tested system.

Sometimes they are a part of the software requirements that form the contract between developers, QA, and stakeholders. They help you to make sure that you are building the right product, and also help ensure that your implementation is correct.

In my experience, most real-life software requirements come in a textual description of feature behavior accompanied with a little graphic (or vice versa), and it’s often not easy to spot problems ahead of time. This creates a so-called “requirements gap”. When allowed to sneak into implementation, this leads to unexpected work and/or the necessity to rewrite the existing code. As such, transforming requirements into acceptance test scenarios early in the process helps the further examination of and discoveries of blind spots — unseen preconditions, corner cases, incomplete, or ambiguous requirements.

Acceptance tests are often thought to be only a part of the “outside testing” (as opposed to “developer testing”) which usually starts when the development transitions to the testing phase. It’s caused by the high-level nature of the tests — you need a functioning system with an interface to be able to perform them. This leads to another problem — as the number of bugs increases with the amount of code, you end up with a fair amount of them before the acceptance tests are able to find them. Needless to say, when faced with an upcoming deadline, bug fixing naturally tends to stick to shortcut solutions with code-degrading consequences.

But what’s important — combined with the so-called “outside-in” (or “top-down”) implementation technique — is that acceptance tests may also drive your implementation.

It’s up to you whether you prefer specialized tools like Cucumber which make writing of acceptance test for non-tech people easy, or you prefer to write them in your programming language as a part of your own developer tests. Either way, you’ll start by filling empty steps with actions — outlining high-level interactions and then step-by-step (or layer-by-layer) discovering and implementing all the necessary classes that enable your system to pass the acceptance test. (Please note that you’ll still need unit tests to make sure your classes work as expected though.)

So how acceptance tests from “outside testing” differ from those that are part of developer tests? The difference is in how the test scenario steps are performed. While outside testing is usually performed (automatically or manually) by accessing UI components, developer testing tests the behavior of correspondent coordinators (i.e. presenters) that are sitting on a stack of application logic and are responsible for the behavior of the whole system.

By using acceptance tests as a blueprint and the “outside-in” principle for implementation of your application, you’ll be able to develop what’s really demanded and lower the number of bugs that make it to the testing phase. If this concept sounds too complicated or tricky, try implementing it first on a small feature within an existing project. Here is a list of useful resources that might help you along your way:

Outside In TDD (parts I-IV) (valuable demonstration of the technique):
https://www.youtube.com/watch?v=XHnuMjah6ps

Acceptance Test Driven Development With Cucumber:
https://www.ctl.io/developers/blog/post/acceptance-tdd-cucumber

Behavior Driven Development (BDD) & Software Testing in Agile Environments:
https://medium.com/agile-vision/behavior-driven-development-bdd-software-testing-in-agile-environments-d5327c0f9e2d

--

--