Behavior Driven Development from the Team to the Enterprise

read
BDD

Behavior Driven Development (BDD) is a process that uses a collaboration between the customer, developer, and tester to create testable specifications of requirements. It is commonly employed at the team level, but it can be extended to the enterprise. This article explains how BDD fits into an enterprise.

Introduction

Behavior Driven Development involves a triad – people with three different perspectives - the customer, the developer, and the tester. They collaborate on a requirement, e.g. a story, to ensure a shared understanding. That understanding is typically expressed as scenarios – a flow through a system. At the team level, the scenarios are focused on small behaviors, e.g. something that might occur on a single web page.

The template for a scenario that is a calculation is:

  • Given some input
  • When a process is executed
  • Then some output

An example might be

  • Given order total is $10.00 and address is in North Carolina
  • When sales tax is computed
  • Then the sales tax is $.75

Scenarios often have a state that is changed by an action or event. The template looks like:

  • Given some state
  • When an action or event occurs
  • Then a new state or an output occurs

An example of this might be

  • Given an order has been created
  • When the user pays for it
  • Then the order becomes paid-for

The behavior which is specified is visible to the customer and should be implementation independent. It should be the same whether the application is written in Java, C#, JavaScript, or any other framework.

These scenarios become the tests for the functionality of a system. These tests represent the requirements – an executable specification. Every externally visible behavior should have a scenario/test for it.

Flow

A user – either human or machine – usually goes through a sequence of steps to achieve a goal. This sequence can be called a workflow, a value stream, a customer journey, business process, or something else. Since this flow is experienced external to the implementation, I’ll call this an “external flow” (see Figure 1 - External Flow) To implement these steps, there will be an “internal flow” between the components (applications, services, microservices, etc.) that make up the implementation.

The external flow is often expressed as a hierarchy. Each step in an overall flow can be broken into smaller steps, as shown in the same figure.

The flow has been shown as linear progressing from one step to the next. In some instances, there may be loop backs from a later step to a previous step. In other instances, steps could be conditionally executed based on some condition.

Fig1

Figure 1 - External Flow

Figure 2 Example of External Flow shows an example using an on-line ordering system. The higher level includes each step for a customer goal of receiving an item on their doorstep. The lower level includes more details for one of the steps. The steps in this level could be broken down even further.Fig2

Figure 2 - Example of External Flow

The behavior at each level can be defined using scenarios that are created by a triad that is appropriate for that level. Each step would have scenarios that represent its particular behavior, as shown on Figure 3 External Behavior

At the team level, the triad might be the product owner, the developer, and the tester. At the higher levels, it might be the product manager, the architect, and the product tester. At still higher levels, it could be the portfolio manager, the portfolio or enterprise architect, and the testing manager.

 
Fig 3

Figure 3 - External Behavior

 

Figure 4 Example of Behavior shows an example from the on-line order system.

Fig 4

Figure 4 - Example of Behavior

 

Here are examples of scenarios at each level:

Get an item

  • Given user wants a widget
  • When user orders widget
  • Then widget appears on user’s doorstep in three days

Order item

  • Given shopping cart has at least one item
  • When user submit order
  • Then order is sent to warehouse for packaging

Compute tax

  • Given order total is $10.00 and address is in North Carolina
  • When sales tax is computed
  • Then the sales tax is $.75

Context and Test Doubles

The context view (Figure 5 Context Diagram) of an external flow shows the users, input, output, and interactions with third-party systems. If there are significant components (e.g. applications) that interact within a context and are useful for understanding, these can also be shown. Otherwise, the implementation circle can be blank.

Fig 5

Figure 5 - Context Diagram

 

An example from an on-line ordering system is shown in Figure 6 Example of Context Diagram

Fig 6

Figure 6 - Example of Context Diagram

When testing scenarios at each level, the context diagram helps in deciding what components require test doubles. Test doubles, like stunt doubles stand in for the real actor, substitute for the real component during testing time. Components that are slow, random, expensive, or take some time to reset their state (e.g. a database record) should have test doubles created for them. Typically, the implementer of the component should be the one to create the test double. This allows for re-use as well as helping to keep the interface of the double in sync with the real component.

In this example, there would be a credit card processor test double. This would take a credit card transaction and return either an accepted or rejected status. It would not actually charge the credit card. Almost every credit card processor provides a test double for the actual processor.

Inside the context diagram, the packaging component interacts with the ordering and the transport components. Test doubles for those two components would send an order to it and receive shipment information from it. They would be invoked in a scenario such as:

  • Given an order is received from ordering
  • When it is processed
  • Then a box with the item and shipping information is created and sent to transport

Components and Behavior

Each external behavior is implemented by one or more internal components (see Figure 7 Component Behavior). Developers design components with behavior which is combined into the external behavior. Some components may consist of smaller components that help implement the enclosing component’s behavior. Each component behavior is defined by one or more scenarios. Examples of components are an application, a service, a microservice, and a language unit such as a class.Fig 7

Figure 7 - Component Behavior

An example of behavior from the on-line ordering system is shown in Figure 8 Example of components. The calculation component might call the location determination and the sales tax data components, or it might call the sales tax data component which then call the location determination component. That is a matter of design. The external behavior of the component remains the same – the calculation of sales tax, as per the scenario:

  • Given order total is $10.00 and address is in North Carolina
  • When sales tax is computed
  • Then the sales tax is $.75
Fig 8

Figure 8 - Example of components

 

Testing and Automation

At every level, scenarios for either external flow or components have been developed. Test doubles have been identified at each level. Prior to implementation, these scenarios can be transformed into automated tests. The tests serve as the executable specification of the requirements that the system must meet. At the lower levels, tests cover all possible scenarios, such as having different sales taxes for different jurisdictions. At the higher levels, the tests cover paths through the external flow to ensure that the connections between steps is correct.

Some tests may be completely automated, some may be manual, or some may be a hybrid. Each may be executed many times or fewer times, depending on the speed of the test, the cost of the test, and the risk of a particular behavior failing.

For example, here are the three scenarios from before:

Get an item

  • Given user wants a widget
  • When user orders widget
  • Then widget appears on user’s doorstep in three days

This needs to be executed to ensure that the entire flow works. This could be run as often as desired, but definitely not for every build of the implementation.

Order item

  • Given shopping cart has at least one item
  • When user submit order
  • Then order is sent to warehouse for packaging

This checks that the ordering step works. It should be run often, but specifically when significant changes occur in its flow or the components which implement that flow

Compute tax

  • Given order total is $10.00 and address is in North Carolina
  • When sales tax is computed
  • Then the sales tax is $.75

This checks that the tax is computed properly. It might be run every build since it’s fast. It should be run when changes are made to the tax calculation components.

Scenario Reuse

Note that the compute tax scenario is from the external flow. However, it is applicable to the tax calculation component as well. Many calculations or business rule scenarios work like this. The scenario needs to be visible to the customer. But it can be run against a component, rather than the whole system. This eliminates redundant testing.

Development / Operations Flow

Figure 9 Development / Deployment Flow shows a simplified flow of implementation from requirement to deployment (The DevOps Pipeline). It indicates where each of the scenarios (external and component) are created and then points to where they are executed. Selected scenarios may be executed during operations, if the application area allows them. For example, the

Get an item

  • Given user wants a widget
  • When user orders widget
  • Then widget appears on user’s doorstep in three days

might be executed in production periodically. However, if the scenario was one that involved financial transactions, then governmental regulations might be hinder doing that in production.

Deployment to testing would involve running scenarios on individual components using test doubles where appropriate, as well as running scenarios on steps in the external flow with and without test doubles.

 
Fig 9

Figure - 9 Development / Deployment Flow

The development / operations flow pipeline is implemented by tools and frameworks. In this case, the implementers are the customers for the tools and frameworks. They can specify scenarios for the overall flow, as well as scenarios for the individual steps. Those scenarios can be used as tests to ensure the pipeline is working properly. An overall flow scenario might be:

  • Given a change in a requirement
  • When the implementation change has be made
  • And the implementation passes the component tests
  • And the implementation passes the external flow tests
  • Then the change shall be deployed to production within one day

The pipeline may be maintained by the same developers who are implementing external flows or by a separate group dedicated to the maintenance and improvement of the pipeline. There should be close collaboration between that group and the developer-users.

Common components

Components may be created specifically for an external scenario or another single component. Alternatively, common components may be designed for use of multiple external scenarios or multiple components, such as shown in Figure 10 Common Components. The customers for this common component are the developers for component 1 and component 2. The triad is those developers, the ones implementing the common component, and the testers. They create scenarios which form the tests of the common component.

Fig 10

Figure 10 - Common Components

 

Other Behaviors

The behaviors described so far relate to the functionality of a system. There are also cross-functional requirements (aka non-functional requirements or quality attributes), as shown in Table 1 Requirement / Testing Matrix (adapted from Gregory/Crispin). The left column shows the functionality requirements/tests that have been described in the previous sections. In the right column are the cross-functional requirements. In the lower right are the quality attribute scenarios, which include performance, security, etc. Scenarios should be created by the triad at the appropriate level. As an example:

  • Given there are 10000 concurrent users
  • When an order is submitted
  • Then it should be sent to packaging within 1 second.

In the upper right are usability and exploratory tests. These tests are typically accomplished manually. The main person who can really say when something is usable is the user. Exploratory testing requires human thinking.

Functionality Cross Functional
External Flow Scenarios Usability / Exploratory
Component Scenarios Quality Attribute Scenarios

 

Table 1 Requirement / Testing Matrix

Summary

Behavior as defined by scenarios can be tested on multiple levels. By defining scenarios prior to implementation, the testing of scenarios becomes part of the design and implementation process. Components that comprise the implementation have scenarios defined by the developers and tested during the build and testing deployment.

About Author

Ken Pugh

Ken Pugh helps companies evolve into lean-agile organizations through training and coaching. His special interests are in collaborating on requirements, delivering business value, and using lean principles to deliver high quality quickly. He has written several programming books, including the 2006 Jolt Award winner Prefactoring: Extreme Abstraction, Extreme Separation, Extreme Readability, and his latest: Lean-Agile Acceptance Test-Driven Development: Better Software Through Collaboration. Ken has trained and coached clients from London to Sydney to Beijing to Hyderabad in ATDD/BDD, TDD, Design Patterns, C, C++, Java, Scrum, Kanban, Lean, SAFe®, and Business Value. He is the co-author of the SAFe® Agile Software Engineering course.

Subscribe to Newsletter

Popular Posts