Easy Approach to Requirements Syntax and the segue to Behavior Driven Development
I was attending a conference six months ago and listened to a talk about quality. During the talk, I was introduced to EARS — Easy Approach to Requirements Syntax. This way of writing requirements struck a chord with me, given my prior experience reading and writing requirement specifications.
When doing agile development, we write User Stories and define Acceptance Criteria.
As a
<role>, I want<goal/desire>so that<benefit>
When doing BDD, we follow this format:
In order to
<receive benefit>, as a<role>, I want<goal/desire>
It’s not always easy to go from user stories and acceptance criteria to start writing tests.
I think that with Easy Approach to Requirements Syntax in place, it will be easier to do Behavior Driven Development.
When doing Hypothesis Driven Development, we follow this format:
We believe
<this capability>Will result in
<this outcome>We will know we have succeeded when
<we see a measurable signal>
So my hypothesis is:
I believe using Easy Approach to Requirements Syntax
Will result in easier implementation of Behavior Driven Development
I will know I have succeeded when business people can actually write the (SpecFlow) feature files themselves ☺
Easy Approach to Requirements Syntax
EARS was created during a case study at Rolls-Royce on requirements for aircraft engine control systems.
They identified eight major problems with writing requirements in an unstructured natural language:
- Ambiguity (a word or phrase has two or more different meanings).
- Vagueness (lack of precision, structure and/or detail).
- Complexity (compound requirements containing complex sub-clauses and/or several interrelated statements).
- Omission (missing requirements, particularly requirements to handle unwanted behavior).
- Duplication (repetition of requirements that are defining the same need).
- Wordiness (use of an unnecessary number of words).
- Inappropriate implementation (statements of how the system should be built, rather than what it should do).
- Untestability (requirements that cannot be proven true or false when the system is implemented).
To overcome or reduce the effects of these problems they came up with a rule set with five simple templates.
Requirements are divided into five types:
- Ubiquitous
- Event-driven
- State-driven
- Unwanted behaviors
- Optional features
Ubiquitous
The
<system name>shall<system response>
Event-driven
When
<optional preconditions><trigger>, the<system name>shall<system response>
State-driven
While
<in a specific state>, the<system name>shall<system response>
Unwanted behaviors
If
<optional preconditions><trigger>, then the<system name>shall<system response>
Optional features
Where
<feature is included>, the<system name>shall<system response>
The Stack Class
Let’s put this to the test with the Stack<T> Class as the example.
This is some of the documentation from MSDN:
Represents a variable size last-in-first-out (LIFO) collection of instances of the same specified type.
The capacity of the
Stack<T>is the number of elements that theStack<T>can store.Countis the number of elements that are actually in theStack<T>.
Three main operations can be performed on a
Stack<T>and its elements:
Pushinserts an element at the top of theStack<T>.Popremoves an element from the top of theStack<T>.Peekreturns an element that is at the top of theStack<T>but does not remove it from theStack<T>.
If we were to write a User Story in BDD format:
In order to store instances of the same specified type in last-in-first-out (LIFO) sequence
As a developer
I want to use a
Stack<T>
If we were to write requirements with EARS templates:
Ubiquitous
The
Stack<T>shall store instances of the same specified type in last-in-first-out (LIFO) order.
The
Stack<T>shall return the number of elements contained when the propertyCountis invoked.
Event-driven
When the method
Pushis invoked, theStack<T>shall insert the element at the top.
When the method
Popis invoked, theStack<T>shall remove and return the element at the top.
When the method
Peekis invoked, theStack<T>shall return the element at the top without removing it.
State-driven
While an element is present, the
Stack<T>shall returntruewhen the methodContainsis invoked.
While an element is not present, the
Stack<T>shall returnfalsewhen the methodContainsis invoked.
Unwanted behaviors
If empty and the method
Popis invoked, then theStack<T>shall throwInvalidOperationException.
If empty and the method
Peekis invoked, then theStack<T>shall throwInvalidOperationException.
Optional features
Where instantiated with a specified collection, the
Stack<T>shall be prepopulated with the elements of the collection.
Behavior Driven Development
Let’s take this to the next level with BDD and SpecFlow.
- Each requirement has its own scenario
- I’ve tagged the scenarios with the type of requirement for clarity
In my opinion, it was easy to write the tests. I copy-and-pasted the requirement to the SpecFlow feature file and then I knew exactly how many scenarios I needed to implement. I think the examples in the scenarios makes the requirements easier to understand and reason about. Maybe this should be called Requirements by Example?
The BDD Cycle
When implementing the production code, we can use The BDD Cycle described in The RSpec Book.

- A photo of page 10 from my copy of The RSpec Book
- As a .NET developer, you can replace Cucumber with SpecFlow and RSpec with Machine.Specifications
The BDD Cycle introduces two levels of testing. We can use SpecFlow to focus on the high-level behavior, the requirements. And use Machine.Specifications to focus on more granular behavior, unit testing code in isolation.
Resources
-
Easy approach to requirements syntax (EARS) by Alistair Mavin et al. The six-page research paper.
-
EARS quick reference sheet [PDF] from Aalto University. A two-page summary.
-
EARS: The Easy Approach to Requirements Syntax [PDF] by John Terzakis. A 66-page presentation on EARS and how it is used at Intel.
-
The source code for the example in this blog post: GitHub