The role of unit tests in test automation
Unit testing is a software development and testing approach in which the smallest testable parts of an application, called units, are individually and independently tested to see if they are operating properly. Unit testing can be done manually but is usually automated. Unit testing is a part of the test-driven development (TDD) methodology that requires developers to first write failing unit tests, then write code in order to change the application until the test passes.
The result of using TDD is that an agile team can accumulate a comprehensive suite of unit tests that can be run at any time to provide feedback that their software is still working. If the new code breaks something and causes a test to fail, TDD also makes it easier to pinpoint the problem, refactor the application and fix the bug.
The AAA Pattern
Unit tests are typically written in a standard format called the AAA (Arrange, Act, Assert) pattern, which helps organize and clarify test code by breaking down a test case into three functional sections:
Arrange: Initialize objects and set the value of the data passed to the test.
Act: Invoke the test case with the arranged parameters.
Assert: Verify the test case behaves as expected.
Example — a unit test that increments the number of products in a shopping cart:
- Arrange: Create an empty shopping cart
- Act: Add a product to the cart
- Assert: Number of products in cart increased by one
Arrange Section
In order to call a software unit and check the result, you first need to put the unit into a known beginning state. When setting up the module to be tested, it may be necessary to surround that module with collaborator modules. For testing purposes, those collaborators could be test modules with actual or made-up data — also known as mock objects or fakes.
Mock objects are simulated objects created by a developer that mimic the behavior of real objects in controlled ways, similar to how crash test dummies simulate human behavior in vehicle impacts. In unit testing, mock objects may be created with fake data because real records don’t exist yet, or because accessing a complete database would slow down tests.
Act Section
This is the part of the test that exercises the unit of code under test by making a function or method call that returns a result or causes a reaction that can be observed.
Assert Section
The assertion section is where you check that you have a result or reaction that matches your expectations.
Following the AAA pattern consistently makes test code easier to read by clearly separating what is being tested from the setup and verification steps.
Behavior-Driven Development (BDD)
A simpler and better way to use the AAA scaffolding is through Behavior-Driven Development (BDD), which BDD pioneer Dan North defines as:
“BDD is a second-generation, outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology. It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters.”
BDD features are usually defined in a GIVEN-WHEN-THEN (GWT) format:
Feature: Items on abandoned shopping carts should be returned to inventory
In order to keep track of inventory
As an on-line store owner
I want to add items back into inventory when an on-line shopping cart is abandoned.
Scenario 1: Shopping cart items not purchased within 30 minutes go back into inventory
Given that a customer puts a black sweater into his shopping cart
And I have three black sweaters in inventory
When he does not complete the purchase within 30 minutes
Then I should have four black sweaters in inventory.
BDD and TDD use equivalent syntax:
Given = Arrange
When = Act
Then = Assert
Outside-in vs. Inside-out Testing
Agile teams generally follow one of two approaches:
Outside-in: Teams start by focusing on the end user’s perspective and describe high-level desired functionality in the form of user stories. Testing then goes inward — code is written to test smaller and smaller components until you reach the unit-test level.
Inside-out (bottom-up): Teams start with unit tests at the lowest level. As the code evolves due to refactoring, testing efforts evolve as the team moves upward to acceptance-level testing (API/service level), and finally to UI testing.
Both approaches are complementary and rely on the related notions of verification and validation:
- Verification: Checks the software against specifications (“Are we building the code right?”)
- Validation: Checks the software against customer expectations (“Are we building the right code?”)
Using TDD and the AAA syntax encourages developers to write small, independent tests and continually refactor their code. BDD supports TDD by bridging the gap between unit tests and higher-level acceptance and integration tests.