What is BDD?
In the 8th chapter, the author quotes various definitions of BDD. I favor the following:
Developers pull features from stakeholders and develop them outside-in. The features are pulled in the order of their business value. The most important feature is implemented first and it's implemented outside in. No time is wasted building unnecessary code.
The evolution of a programmer that is learning TDD is a good guidance in understanding the BDD:
- Most developers start by writing unit tests to existing code.
- After some practise they learn about the advantages of writing the test first. They might learn about good testing practices like AAA, stubs and factories at this point.
- They realise that TDD can be used as a design tool to discover the API of the production code.
- Eventually they recognise that TDD is not about testing. It's about defining behavior and writing specifications that serve as living documentation. They also discover mocks and outside-in development.
Principles of BDD
Characteristic feature of BDD is using "baby steps" to achieve tight feedback loop. In practice, this means writing a single spec knowing that it will fail, writing the missing code logic, returning to the spec. This continues switching between code and test cases results in less scanning of the code.
You want to achieve Red/Green/Refactor routine:
- Are all specs green (after the implementation phase)?
- Is one spec red (after coding another spec)?
The idea is to ensure that the test really works and can catch an error.
Instead of filling in a real implementation, you can fake it with a dummy value.
You know that your implementation isn't ready, but all the specs are showing green. This implies that there is a spec example missing.
You proceed until you've covered just enough test cases to produce a general solution.
The idea is to gain moment and insight into how the algorithm should behave.
Behaviour Driven Development is characterized by:
- Spec/Error driven coding.
- Using baby-steps to achieve a "tight feedback-loop".
- Follow the Red-Green-Refactor (RGR) cycle to avoid the false-positives.
- Software design driven by needs.
- RGR cycle enhances the separation of the workload. Red is for the interface, green is for the implementation.
- Specs enable code refactoring.
- Automated tests that read like documentation. Prefer DAMP over DRY.
- Altering internal code structure without affecting the external behaviour.
- Descriptive and Meaningful Phrases.
BDD in Practice
Using Test Doubles
Empty object that does nothing.
- Objects are passed around but never actually used. Usually they are just used to fill parameter lists.1
Method designed just for testing.
- Stub objects provide a valid response, but it's static – no matter what input you pass in, you'll always get the same response.2
- An object that provides predefined answers to method calls.3
- Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it "sent".4
- A stub will never cause a test to fail.5
- Stub is simple fake object. It just makes sure test runs smoothly.6
Object designed just for testing.
- Mock objects are used in mock test cases – they validate that certain methods are called on those objects.2
- An object on which you set expectations.3
Method used to instantiate the SUT object with canonical values, overwriting only the properties relevant to the test case.
See https://github.com/petejkim/factory-lady, Object Mother and Test Data Builders.
Every spec needs these three parts and nothing more – in this order:
- Setup, e.g. create an object.
- Execute, i.e. introduce change to the object state.
- Expect, i.e. state an expectation of what should have happened.
You might have also heard Given, When, Then (GWT):
- Given a condition.
- When I do something.
- Then I expect something to happen.
The two are identical. The phrasing of the latter might be easier to comprehend.
As a result, your specs must adhere to the following rules:
- The spec must implement not more than one execution.
- The spec must abstain from several expectations.
- Do not use if-statement that lead to multiple execution and expectation branches – write several specs instead.
In TDD world it is known as the Arrange, Act, Assert (AAA) pattern.
The specs are organised either per Feature or per Fixture.
This way of organizing a spec is also referred to as "by topic".
The benefit of organizing your spec per feature makes it easier to write.
This way or organizing a spec is also referred to as "by example data".
The benefit of organizing your spec per fixture makes the resulting spec more descriptive and easier to read. The additional benefit is because all your examples share the same example data, you need to state it only once (e.g. using
beforeEach setup function).
High and Low Level Spec
BDD project usually starts with writing the outer circle (high level specs) and then proceeding the implementation of the inner circle (low level specs).
You start with the implementation of the specs that have meaning to the business using hypothetical components (aka. acceptance test):
Acceptance test is a high level spec that describes a scenario from the view of an application user. In contrast to the low level spec, a high level spec does not have an SUT.
The upside of the outside-in development is that you never write code that will become redundant (the requirement can be traced back to the spec). The downside is that you cannot run the test cases until the implementation is complete.
- They help to become aware of bugs – all spec violations are considered a bug (defect awareness).
- They are most useful when shown to stakeholders.
- There are usually only a few. they don't include all special cases. their main purpose is to provide an overview of the required functionality.
You start with the basic components that make up the application:
- They help to find specific bugs (localization).
- They are most useful to the developers who maintain the code later.
- There are usually a lot of low level specs since they need to cover all the low level details, special and edge cases.