Methods & Tools Software Development Magazine

Software Development Magazine - Project Management, Programming, Software Testing

Scrum Expert - Articles, tools, videos, news and other resources on Agile, Scrum and Kanban

Fundamentals of Enterprise-Scale Behaviour-Driven Development (BDD)

Adam Craven, @ChannelAdam, https://ChannelAdam.com

Introduction

As a long-time advocate of Behaviour-Driven Development (BDD) and having been a technical lead and coach of BDD enterprise software development teams since 2010, I have tried many different variations of the process of BDD and the writing and implementation of Behaviour Specifications (also known as scenarios).

I have specialised in, experienced first-hand, fine-tuned and witnessed many practices that have repeatedly proven to be beneficial in both small and large-scale solutions. As such, I have distilled and compiled these worthwhile practices into The Behaviour Specification Handbook [1] - a free online resource that contains my prescriptive methodology for successfully applying BDD at any scale, and practical tips and suggestions for writing and implementing effective Behaviour Specifications.

This article presents some of those core fundamentals and practical tips.

The Fundamentals

This section details the mindset required and the core fundamentals to be understood in order to successfully apply BDD at any scale. We will start at the beginning, with a quick overview of BDD.

Overview of BDD

The software engineering practices and processes of Behaviour-Driven Development (BDD) that I promote focuses a team on delivering software solutions with high business value, higher quality and less defects. BDD encourages and helps to facilitate close collaboration and clear communication between team members and with business stakeholders.

With BDD, it is possible to deliver solutions that better satisfy actual business requirements and have a lower on-going maintenance and total cost to the business. This can only be achieved however, if the team maintains professional discipline, follows the processes and performs the software development activities, in the recommended order.

One of the key practices is the Behaviour Specification writing sessions. Behaviour Specification writing sessions should be attended by multiple roles in the team, before the coding of a specific piece of functionality is started. This one practice, guided by concrete examples or use cases, has the simple and significant effects of optimising the design, review, and testing feedback cycle by:

  • Ensuring what is to be delivered is actually required;
  • Ensuring what is to be delivered is valuable to the business;
  • Ensuring what is to be delivered is within the agreed scope of functionality;
  • Ensuring what is to be delivered is well understood by the team;
  • Clearly specifying the required behaviour of each software component in everyday common business language that anyone in the team (including business stakeholders) can understand; and
  • Each attendee mentally testing the requirements and software component specifications against the proposed design.

The Behaviour Specification writing sessions encourage and cause important discussions, questions and healthy debates to take place. The result is a common and well-understood, mentally tested and agreed solution - before development has started. There is therefore a significant reduction in the risk of defects and missing or ambiguous functionality early in the software development lifecycle, when it is the cheapest and most appropriate time to have those discussions and identify potential issues.

The clear communication is achieved by everyone in the team discussing required software behaviour or functionality in the business's everyday common language. If for some reason the common business language unfortunately is ambiguous in some areas, then that is a wonderful opportunity to highlight the risk and show leadership to the business by suggesting and helping to implement some positive, clarifying terminology changes!

As you know, team members will come and go over time. Current and future team members however, may not have the same level of competency, business analysis capability or technical skills. This is why the software specifications are written in the everyday language of the business. Every team member (or interested business stakeholder) who understands the business (the lowest common denominator) - regardless of their technical competency - should be able to understand the functionality that is to be or has been delivered. The total cost over time of software development, maintenance, resourcing and on-boarding is reduced because of this simple concept.

The resulting artefacts typically produced in the process of BDD include living documentation (also known as executable program specifications) and other test assets, such as the test run reports for the executed specifications.

Scaling BDD to Large Solutions

Many teams struggle to effectively apply BDD, mostly because of a lack of knowledge and understanding. This article and The Behaviour Specification Handbook [1] aims to help fill that gap. It is easy to find examples of Behaviour Specifications, especially on the reference documentation sites of the various BDD libraries and tools. Those types of examples may be fine for that tool’s documentation, demonstrations, presentations or small applications, however the style of many of those examples do not necessarily scale up and work well in more complicated software solutions.

An Enterprise-Scale Example

Have you ever worked in an enterprise solution delivery team where there are multiple systems, applications and components involved in a single User Story?

For example, say that there is the following User Story.

As an online customer

I want to see a summary of all my incomplete orders

So that I can effectively track my orders

This User Story sounds simple, right? Unfortunately, software development in an enterprise typically is never as simple as one would like!

In this example, the technical solution that is required by an unspecified enterprise just happens to involve many different layers and technologies:

  • The HTML, CSS and JavaScript of the Web Application;
  • The RESTful API Controller layer of the Web Application;
  • A Service-Oriented Architecture (SOA) Business Service that integrates with a legacy Order Management System; and
  • A Web Service that is exposed by the legacy Order Management System.

This is not an uncommon technical landscape!

Which Behaviours?

From a behaviour perspective, there are many potential behaviours in the above example:

  • The visual behaviours of the User Interface - such as hiding and showing visual elements and displaying pretty animations;
  • The logic behaviours of the User Interface - such as performing screen navigation, calling the API Controller layer and handling errors;
  • The behaviours of the RESTful API Controller that calls the Business Service;
  • The behaviours of the Business Service that invokes the legacy Order Management System’s Web Service; and
  • The behaviours of the legacy Order Management System's Web Service.

Before we go any further, let me ask you a quick question. When you thought of BDD in the context of this "simple" example of showing a customer their incomplete orders, which set of behaviours from the list above did you think of originally?

In my experience, many teams tend to focus just on the behaviours of the User Interface - after all, that is the focus of the User Story. When that happens, many of the benefits of BDD are not realised, and one or both of the following two issues typically occurs.

The first type of issue is that the team may think it only necessary to write Behaviour Specifications for the User Interface component. The behaviours in this case are then generally written in the style of End-to-End tests which typically focus on exercising multiple variations, combinations and boundaries of data. Unfortunately, that approach leads to the creation of an inverted Testing Pyramid [2] with too many expensive End-to-End style tests, compared with Unit Tests.

The second type of issue that could occur is a lack of quality assurance and test coverage of the entire solution landscape. If the team is only writing Behaviour Specifications for the User Interface component for example, this leads to a few quality assurance and maintainability questions:

  • How are the other systems and components in the solution being tested?
  • What tests are being written for those other components?
  • Is the Testing Pyramid being adhered to, and is testing being performed at the most appropriate level?
  • Are all of those test cases valuable, valid and correct?
  • Who knows about and understands the intended purpose of the other tests?
  • If they exist, are these tests duplicating testing effort elsewhere?
  • Who is implementing and maintaining those other tests?
  • How are the other tests being managed, communicated, executed and reported on?

Perhaps some developers are being professional and are implementing automated tests for the code they have written. Perhaps their tests are valuable, or perhaps a developer has gone off on a tangent and is wasting time and money (and nobody knows!). Part of the problem is that the team as a whole does not have enough understanding and visibility over the tests (especially Unit Tests) that developers write, and that can lead to wasted and duplicated effort, as well as negatively affecting team cohesion.

To best realise many of the benefits of BDD, all the behaviours applicable to a User Story should be specified - not just for instance, the User Interface behaviours.

Managing Behaviours

Decomposing a large solution or problem domain into a number of more manageable chunks is an age-old approach that scales up and works. The approach that I recommend is to decompose a solution into a number of significant software components and then work on the Behaviour Specifications of each component, one-by-one.

In order to prevent confusion, each component should be considered individually, and the behaviours of each component should remain separated from those of other components.

This suggested approach scales, is manageable, can be understood easily and it has repeatedly worked! In addition, each software component individually can be tested and reported on, and the Testing Pyramid can be maintained.

What is a Software Component?

A software component is a logical grouping of functionality that is represented as an application, sub-system, service, micro-service, library, module, or any sort of implementation of part of an architectural layer. The granularity and boundaries between software components really depends on what makes logical sense. As a general guideline, a software component typically is larger than one class or module - although this guideline may not apply in the case of micro-services.

For example, a software component could be the functionality that provides the User Interface for a screen, or it could be the functionality of a back-end service operation. A software component also could be a specific part of a data access layer or repository.

Teams should carefully consider the design of their software components, as these choices affect later stages of the software development lifecycle. If a team ever struggles with maintaining a well-structured Testing Pyramid, consider reviewing the boundaries of the software components. Splitting a large component into many, each with less responsibilities, could open the door to simplified Behaviour Specifications, a better Testing Pyramid, easier comprehension of functionality, and a lower total cost of ownership.

What are Behaviour Specifications?

Behaviour Specifications conveniently and efficiently fulfil multiple purposes:

  • Written in everyday business language, they comprehensively and unambiguously specify the required functionality to be delivered, as agreed by the team;
  • They are the specifications that developers should refer to when they write code. The only code that should be written by a developer is code to satisfy the Behaviour Specifications. Remember this little motto: "No Behaviour Specification? No Code! No Test!";
  • They also represent the complete set of test cases that are needed for the given functionality;
  • They are the living, up-to-date, as-built documentation, for the entire team (and potentially other parts of the business), about the functionality of each software component; and
  • When implemented as automated tests, they are the executable documentation that becomes part of a suite of test assets.

Behaviour Specifications are NOT part of a formal requirements document or legal contract - they are as-built documentation and test assets. Requirements are a prerequisite input for the process of specifying behaviours.

No Other Test Cases are Necessary!

Quite simply, no test cases other than those represented by the Behaviour Specifications are necessary.

Behaviour Specifications are Comprehensive

Behaviour Specifications literally are the functional specifications of software components. The specifications for a given software component should be comprehensive and unambiguous. If that is not the case, then team members including developers and testers will not know about all the functionality they are supposed to deliver, and the solution delivery will fail.

No other test cases are required! The logic of this statement is simple - since program specifications need to be comprehensive, then no other tests are needed as all possible test cases already should be covered by the specifications.

About Test Independence

With this statement about no other test cases being necessary, I am referring specifically to and recommending a software development team structure and operating model where there are business analysts, developers and test analysts closely collaborating. The goals of the team are to achieve a cost-effective, streamlined, successful delivery of a solution with an agreed level of quality assurance.

Due to the close collaboration of the team members and common desire to prevent unnecessary duplication of testing effort, the test analysts are operating within the same team and cannot, as described by the International Software Testing Qualifications Board (ISTQB) [3], provide a high-level of test independence. Depending on how the team operates however, there certainly could be a medium-level of test independence. For many clients, projects and solutions, this approach is both efficient and sufficient, and it is specifically within this operating context that I state no other test cases are necessary.

For life-critical systems and solutions however, more rigour is required and there should be a high-level of testing independence. In that case, a separate, independent team or company would actually perform testing in addition to that performed by the software development team. The additional testing certainly overlaps and duplicates significant amounts of the testing effort performed by the development team. It also requires a lot more time, resources and cost. This additional testing however, is performed outside of the software development team, outside the practice of BDD, and is thus outside the scope of this article and the presented recommendations.

Developers: No Other Automated Tests!

Developers in particular need to understand that they should not write any automated tests other than those that are specified by the Behaviour Specifications. Developers love writing code, and many love writing automated tests (although contrarily, many do not!). Even when the team together has specified all the required behaviours, some developers can still feel as if they should write more automated tests when they are coding.

If this is the case, it is possible that the developers need to better understand the premise and comparative benefits of Behaviour-Driven Development and also the concept of Equivalence Partitioning [4].

Benefits of Behaviour-Driven Development

In Test-Driven Development, it is common for developers to write many automated tests for each method within each different class or module. Some of the issues with that approach include:

  • A high maintenance cost of automated test assets due to brittle tests that must be refactored when the code is refactored;
  • Communication issues between developers and the rest of the team - some developers can find it difficult to effectively describe the automated tests they are coding on a daily basis. This lack of communication can lead to other team members questioning and arguing the value of such activities;
  • Developers can struggle to estimate when they will finish all of their automated testing - since the answer is "it depends", depending on the number of classes and methods written; and
  • Duplicated testing effort by test analysts - since the automated tests implemented by developers typically are not visible to the non-developers in the team, the testers usually perform many tests that may already be covered by the developer's automated tests.

In contrast, some of the advantages of the Behaviour-Driven Development approach to implementing tests include:

  • The team agrees, up-front, on the behaviours to be implemented and afterwards there is no room for a debate about the value of any of the tests;
  • The team decides, up-front, which behaviours are implemented by the developers and which behaviours are performed by the testers;
  • The team can more accurately provide estimates to management - since the tests are not dependent on the number of classes and methods coded;
  • There typically are fewer tests and they are less brittle. When a refactoring of the implementation code takes place, there are less refactorings of the test code to perform. Even though there are less tests, there still can be similar test coverage - because overall behaviour is being tested - not individual method functionality; and
  • Test analysts (and everyone) have visibility of all the tests of each software component and therefore should not unknowingly duplicate testing effort.

Overview of the Gherkin Language

Behaviour Specifications can be written in different ways, and the most renowned is with the Gherkin language [5].

The Gherkin language is the most widely used and recognised business-readable, domain-specific language (DSL) for specifying behaviours. Each behaviour is defined as an individual "Scenario". A "Scenario" has a description that summarises the behaviour being specified. The behaviour is then further defined as a series of steps to be executed in the order in which they are written - just like the steps of a test case.

The syntax of the Gherkin language specifies that each step begins with one of the following keywords: "Given", "When", "Then", "And" or "But". The "Given" steps must be specified before the "When" steps, which themselves must be specified before the "Then" steps. The "Given" steps define the prerequisite state that is required for the behaviour to manifest. The "When" steps define how to initiate the behaviour being specified. The "Then" steps define how to verify that the behaviour performed as expected. Steps that begin with "And" or "But" merely allow for more details to be specified and extend the meaning of the proceeding "Given", "When" or "Then" step.

The Behaviour Specifications are logically grouped as a "Feature". A "Feature" also has a short description that summarises the scope of the logical grouping of behaviours. While not necessarily specific to the Gherkin language (it depends on the implementation of the tooling that parses the Gherkin language), each "Feature" typically is stored within a file with a suffix of ".feature".

There are various tools in each different technology platform for parsing and executing the Gherkin-based "Feature" files. For instance, in Ruby there is Turnip [6], Spinach [7] and Cucumber [8], in .NET there is SpecFlow [9], JBehave [10] for Java, Behat [11] for PHP, Gauge [12], and many others.

What is a Behaviour Specification Description?

Behaviour Specification Description (also known in Gherkin as a "Scenario" description) is a one-line, simple, precise summary of an expected behaviour of a software component. I recommend the description be worded in commonly accepted, everyday business language, without specifying internal technical implementation details. Each description must be sufficiently precise so that all the Behaviour Specifications of a given software component can be clearly differentiated.

Example Behaviour Specification Descriptions

As an example, let us assume that there is a User Interface component that invokes a back-end service to perform validation of a postal address. The Behaviour Specification Descriptions of this User Interface component could include:

  • Should allow a specified postal address to be submitted for validation;
  • Should transform the submitted address data into a format that is required by the address validation service;
  • Should invoke the address validation service with the appropriately formatted address data;
  • Should provide a notification of a technical error when there is an issue communicating with the address validation service;
  • Should provide a notification of an invalid postal address when the address validation service determines the address is invalid; and
  • Should provide a notification of a valid postal address when the address validation service determines the address is valid.

The behaviours of this User Interface component explicitly do NOT include anything related to how the back-end service performs the validation. Those types of concerns are only the service's behaviours and are not the concern of this User Interface component.

Please make a note of these example Behaviour Specification Descriptions, as they will be referred to in some of the following tips.

Tips for Effective Behaviour Specifications

With the fundamentals and required mindset understood, now we can proceed with some general tips for writing effective Behaviour Specifications.

The Software Component Context is King

When identifying, reading or writing Behaviour Specifications, the context (knowing which software component within the solution is being specified) is the most important thing to understand. It is essential to have a clear understanding of the boundary and responsibility of each software component, otherwise the Behaviour Specifications cannot be correctly understood. A low-level technical design and diagram certainly can help.

Feature Files

Feature file contains the Behaviour Specifications for a logical grouping of related functionality. I recommend having one feature file for each different software component involved in the same logical grouping of functionality.

At the top of a Gherkin feature file is the feature's name or title. I recommend suffixing the feature’s name with the type of the software component (such as User Interface, Service Layer, etc) so that feature files for the same related functionality are clearly discernible by the type of software component that implements them.

Different Feature Files, Same Behaviours

When following the recommendations presented in this article and in The Behaviour Specification Handbook [1], it is often the case that some of the exact same behaviour descriptions and Gherkin can be reused in the different feature files of each software component involved in the same logical grouping of functionality. For example, the feature files for both the User Interface and the back-end service layer involved in the same logical grouping of functionality could have the same behaviour descriptions and Gherkin. The back-end service may have additional behaviours for extra business logic that it alone must perform, but as a starting point, the initial set of behaviours could be the same.

As stated earlier, behaviours must always be considered and understood from within the context of and from the perspective of their software component / feature file. Even though the wording of the behaviour may be the same, the actual meaning and implementation will differ, based upon that context. This is why it is beneficial to suffix the feature name with the type of software component, as it is an important contextual differentiator.

Start Behaviour Descriptions with "Should"

In order to write an effective Behaviour Specification Description, describe each behaviour in a sentence beginning with the word "Should".

The word "Should" at the beginning of a sentence helps to quickly focus both the author and reader on the actual intent of the behaviour. In the English language, behaviours generally are described with verbs. The next set of words grammatically after "Should" typically is a verb or adverb. By starting with "Should", the structure of the English language is forcing the optimisation of the descriptions so that we can immediately focus on the intent of the actual behaviour and more quickly understand how it may differ from other behaviours.

A template format for the recommended wording of a Behaviour Specification Description is:

Should [action] when [criteria]

The first part of the description is the behaviour or action to perform, and the second part is the criteria required for the action to occur.

For example:

Should calculate the invoice amount when an order item is added to an order

This description is clear and intention-revealing about the behaviour. A reader immediately knows and understands that the behaviour is to "calculate the invoice amount", and the criteria for this occurring is when "an order item is added to an order".

Applying a standard such as beginning Behaviour Specification Descriptions with the word "Should" helps teams to write more useful and intention-revealing descriptions and naturally fall into the "pit of success".

Different from a Test Case

A Behaviour Specification Description is subtly different from a description of a Test Case. A traditionally written description of a Test Case is authored from a different perspective that typically aims to summarise the scenario and steps that are taken.

For example, a contrasting traditional Test Case description for the example behaviour above would typically be:

Adding an order item

Hopefully you can appreciate the subtle differences between Behaviour Specification Descriptions and Test Case descriptions. The description of a Test Case is often shorter and at a higher summary level.

Behaviour Specifications are Not Legal Requirements

Requirements are a prerequisite to the identification and writing of Behaviour Specifications.

I acknowledge that a pedantic reader of a Behaviour Specification Description could choose to interpret "Should do XYZ always" in English as "it should do XYZ always, BUT because the word 'should' was used, it does not always have to... and therefore there is space for unintended behaviour". This same type of reader would suggest tightening the terminology in order to make it completely unambiguous and fit for a formally signed business or legal contract, by prefixing the description with the word "Must", "Shall" or "Will" instead of "Should".

Behaviour Specifications however, are not part of a formal business contract (such as a requirements document) that requires legally precise wording and change management. Behaviour Specifications are only as-built, living documentation and test assets, and using the word "Should" is perfectly acceptable.

The Subtle Aspect of "Should"

In case you need any more convincing, Dan North, the originator of BDD, in Introducing BDD [13], eloquently adds more weight to the argument for using the word "Should":

"A more subtle aspect of the word 'should' becomes apparent when compared with the more formal alternatives of 'will' or 'shall'. 'Should' implicitly allows you to challenge the premise of the test: "Should it? Really?" This makes it easier to decide whether a test is failing due to a bug you have introduced or simply because your previous assumptions about the system's behaviour are now incorrect."

Write In Everyday Business Language

A large focus of BDD is about clear and effective team communication. Not all members of the team have or will have a deep technical understanding. It is in the best interest of the business if everyone is enabled and given the opportunity to understand the logical functionality of each software component.

On a daily basis, the team normally should be communicating with the rest of the business in commonly accepted business terminology. Similarly, the behaviours should be written - as much as possible - in the same accepted style of business language. By doing this, all members of a software development or solution delivery team, including developers, analysts and stakeholders, should be able to understand the Behaviour Specifications.

Agree on Business Terminology

The team needs to have a clear understanding about what is commonly accepted business terminology versus technical terminology. In general, communication cannot take place effectively unless agreed protocols are followed. An agreed glossary of business terminology is the basis for an effective communication protocol within a business and is useful reference material - especially for on-boarding new employees.

Such an approach promotes more consistent communication within the solution delivery and support teams - and with wider parts of the business. One tangible, beneficial result of using commonly accepted business language is the minimisation of the costs to train new staff.

Be Professional

Some developers may initially consider working with the Gherkin language and the use of a feature file as a burden. These developers may argue that the usage of the feature file slows them down and that they can much more quickly write automated tests without Gherkin or Behaviour Specifications.

Typically however, such developers are new to the use of Gherkin and have not proceeded past the learning curve. Additionally, some stereotypical technical people can have difficulty in translating technical functionality into common business language for discussion with non-technical people. Because of these reasons, some developers may find they are initially slower in this area and that can be a cause of personal frustration.

We all need to remember to be professional and look after the best interest of the business. It is in the business's best interest if the team writes behaviours in their business language, not just for the current set of skills in the team, but for the wide variation of skills that future team members may or may not have. The business language is the lowest common denominator language - not technical terminology.

If the business is primarily product focused or technical in nature, such as one that creates software languages or compilers, then the business language and technical language may be one and the same. By all means, write the behaviours in the business language, which in that case may be quite technical.

Use Natural Language

As a guideline, I highly recommend using the natural language, logical names of artefacts, as opposed to the technical, implementation-specific terminology.

For example, say that there is a business service operation for processing credit card payments. In a specification, I recommend writing natural language, such as "the service that processes a credit card payment", instead of a more technical implementation name such as "the CreditCardService.ProcessPayment operation".

Similarly, to refer to a particular system in a specification, such as a fictional system named "Insurance Policies 4 Me", write a natural language, logical name that is appropriate for the business, such as "the insurance policy system".

Some Technical Terminology becomes Mainstream

At certain points in time, some terminology that originates from the technical realm does actually make its way into business and everyday mainstream vernacular. The television and movie industry no doubt have played a huge role in helping to mainstream many technical concepts.

Nothing in the last decade highlights this better than the world of personal and mobile computing. The concept of an "app" or "application" is now common, mainstream vocabulary. From young children to retirees, almost everyone now has access to a mobile phone, tablet or personal computing device, and everyone is familiar with "apps" and an "app store". Security mechanisms to ensure authorised access to a personal device - such as user credentials, pin numbers, and swipe patterns - also are now mainstream concepts.

What Is Technical Terminology?

Noting that language and commonly accepted vocabulary changes over time, what is technical terminology and what is not?

That is a good question! The answer is that it depends on each business. What might be considered "technical" in one business at a specific moment in time, might be common business language right now at another business.

In addition, there are many concepts that began in and are commonly understood in real-life. Many real-life and mathematical concepts have been used as the basis for technical lingo. An example of this is a "queue" of items. A queue is a well understood real-life concept. Many people stand and wait in queues or first-in-first-out (FIFO) lines at the shopping centre checkout on a regular basis. If a technical team determines that there are messages that need to be queued as part of a solution design, then please say that in the Behaviour Specification, as it is natural everyday language!

Human perception also adds complexity to the discussion. Different businesses, teams and people can have both valid and differing views about how "technical" some "technical terminology" actually is. Every individual has a different perspective and understanding of the world, and their level of tolerance for technical terminology differs.

The Art of Writing

In the end, finding simple, intuitive and natural words to use when writing a Behaviour Specification sometimes can be thought of as an "art". There can be a delicate balance between not using language that is too technical or specific, and at the other end of the spectrum, using language that is too generic such that the reader cannot grasp the essence or actual purpose of the behaviour. This balance can be a challenge - especially for novices - but it is overcome with an agreed business terminology glossary, some good examples and a little practice.

Avoid Specifying Internal Technical Implementation Details

There is no need to specify the internal technical implementation details of a software component in the Behaviour Specifications. To be very clear, let me emphasise the word "internal", in contrast with any publicly exposed, outward-facing behaviour or other mandated functionality, such as business rules.

If the implementation of a component needs to be refactored or changed, the words used in the Behaviour Specifications should NOT need to be changed. The words used in a Behaviour Specification should only need to change if there is a change to a core business requirement.

Behaviour Specifications can become unnecessarily brittle and potentially short-lived due to tight coupling that is created when any internal technical implementation concerns are specified. In other words, if internal technical implementation details are specified in the Gherkin, then the cost of maintenance and future enhancements will increase unnecessarily, since it could have been avoided in the first place.

Internal Technical Details - Example #1

For example, say that the low-level technical design has identified there is a component that must store and retrieve items in memory. Perhaps the component uses a Dictionary data structure to store the data, or alternatively, perhaps it uses an Ordered List data structure.

In this example, the type of data structure internally used by the component is completely irrelevant, and the fact that a data structure exists at all also is completely irrelevant in the Behaviour Specification. The only concerns that are relevant are the behaviours related to successfully and unsuccessfully adding and retrieving items, and also perhaps some non-functional performance requirements.

Internal Technical Details - Example #2

In this example, say that there is a behaviour specifying that a component should persist its data, but it unfortunately also says the words "in a database". The word "database" is an internal implementation detail that makes the Behaviour Specification unnecessarily brittle, and it is unnecessary information to include. If in the future, the implementation is changed to store the data in a file in a file system, the wording of this Behaviour Specification also would have to be changed. This is inefficient, costly and can be avoided!

Alternatively, the wording "data store" is an example of a preferred, generic term that cleverly encapsulates any changes or volatility in implementation. When using the words "data store", if the internal technical design of the component changes from using a database to storing the data in a file, the wording of the Behaviour Specification does not need to be changed. A Behaviour Specification that says "data store" instead of "database" is resilient to future implementation changes.

Internal Technical Details - Example #3

In the example descriptions in the earlier section "What is a Behaviour Specification Description?", hopefully you noted that there is no indication of how the behaviours are technically implemented internally. In fact, just by reading those descriptions, you would not even know that they are specifically describing the behaviour of a User Interface component. That is intentional! Unlike many Gherkin examples that can be found in BDD literature, there is no typical User Interface wording about "screens", "pages", "fields", "buttons", "clicks", or "navigation" etc, nor is there any need for those types of words.

Internal Technical Details - Example #4

The example descriptions in the earlier section "What is a Behaviour Specification Description?" also do not specify the particular technical mechanism by which the address validation service is invoked - for instance, as a Web Service call. That would be internal implementation detail!

It is possible that the solution delivery team could decide it is best in the short-term to implement a tactical solution. In this example, that tactical solution is chosen to be a monkey that physically runs down the corridor and hands a human a piece of paper that has an address printed on it which is to be verified. Whatever tactical or strategic approach is taken, the Behaviour Specification wording should not need to be altered!

To recap, it does not matter how the behaviour is implemented internally within a component. The wording of the Behaviour Specification should only need to change if there is a change to the core business requirement. Behaviour Specifications should be long-lasting and resilient by encapsulating internal implementation volatility. For anyone who is interested in the current, actual technical implementation details, that can be understood from and seen in the test run output.

Avoid "I"

A quick search for example Behaviour Specifications predominantly reveals behaviours that use the word "I". If you ever come across this, please ask the following question.

"In the context of that software component, who or what really is 'I' and are they important?"

As discussed further below, a Behaviour Specification that uses the word "I" is generally not focused or specific enough on the actual behaviour and introduces potential ambiguity and confusion. Just avoid the word "I"!

There are a few reasons why the usage of the word "I" could be so common:

  • Due to having a mindset of "testing" versus "specification";
  • Due to having a prevalent focus on specifying User Interface or End-to-End behaviours; and
  • Due to copying the style of User Stories.

Due to a Mindset of Testing

For instance, the Agile Alliance website has a description of the Given-When-Then syntax [14], and their example uses the word "I". The same page interestingly states that the Given-When-Then syntax is "intended to guide the writing of acceptance tests for a User Story". By using the word "test" in their description of the Given-When-Then syntax, their mindset is clearly still focused on testing - rather than specification.

Acceptance Tests [15] pre-date Behaviour-Driven Development, with BDD being an evolution of previous methods, focusing more on "specifying behaviour" rather than talking about "tests". There is a powerful and yet subtle conceptual difference in perspective between "writing tests" and "specifying behaviour".

This is one reason why my recommendations differ, as my guidelines come from a mindset focused on specifications - not just testing.

Due to a Focus on User Interfaces

Another possible reason for the common usage of the word "I" is the significant amount of User Interface-focused behaviour examples, where there is an actor interacting with a system.

The following "When" step illustrates a typical, inadvisable, easily found example of a User Interface-focused step.

When I click on the Submit button<
Then the transaction details are sent to the service that records the transaction

One reason why I recommend against using the word "I" is because it subtly focuses attention on the actor’s actions and focuses less on the behaviour of the component being specified. In the above example, in the English language, the subject of the phrase is "I". The primary focus is thus needlessly on the actor performing the action of clicking, rather than the subject being the more important aspect that the button’s click behaviour was triggered.

Another reason to avoid "I" is because it begs many other questions to be asked, such as:

  • Who actually is "I"? Is "I" a guest user to the system, or my logged-in user profile?
  • What would be the behaviour if someone else performed the action? For instance, what happens if John does it? How should the component behave then? That information unfortunately is not specified and is therefore ambiguous.

If the purpose of the Behaviour Specification was however to define the behaviour that occurs when a user profile with a specific security role or permission performs the action, then at least the logical name of the security role should be specified, and the word "I" still is unnecessary and not beneficial.

Let us now take this example to the next level - and remove the word "I".

When the Submit button is clicked
Then the transaction details are sent to the service that records the transaction

The wording above is now much better focused on the action of clicking the Submit button, without the distraction and unimportant details (in this case) of who clicks the button.

We can do even better than this though! Remembering that the software component context is key to the understanding of Behaviour Specifications, and understanding that this particular behaviour is written in and from the context of a User Interface component, we can rewrite the "When" step as follows.

When a trigger occurs to submit the transaction details
Then the transaction details are sent to the service that records the transaction

Within the context of a User Interface component and feature file, the information about a "Submit button" and "clicking" is unnecessary implementation detail. With the exception of specifying security rules, it does not matter who clicks the button, what type of button it is, whether it is a button or any other type of User Interface widget, and whether the widget is mouse clicked, tapped or keyboard, voice or thought activated. What is important for this behaviour however, is that something happened to trigger the recording of transaction details and that there was an attempt to invoke an appropriate business service.

Due to Copying the Style of User Stories

A User Story is a precise and short expression of a requirement by a particular type of business role. As such, the syntax begins by specifying the role of the actor - for instance, "As an accounts manager" - and the rest of the story is written and understood from within the context and perspective of an actor with that specified role. Additionally, a User Story can encompass the functionality provided by multiple software components and systems.

In contrast, a Behaviour Specification is a specification about part of one software component, and that behaviour may apply to many User Stories. Within the context and perspective of that software component, if the word "I" was used, it should only refer to the software component itself. The problem is that it really does not make sense to refer to a software component in that way in the first place.

User Stories and Behaviour Specifications have different purposes. Confusion ensues if one incorrectly mixes the concepts and tries to think about and understand a Behaviour Specification from within the context of a specified role of a User Story.

For the sake of simplicity, and to reduce potential confusion and ambiguity, avoid using the word "I" in Behaviour Specifications.

Have Different Feature Files for Different User Interfaces

How does one specify the various User Interface behaviours for keyboard or voice activation, touch-enabled device taps and mouse clicks?

The short answer is: the software component context is king!

Each different interface should be considered another dimensional axis of the same set of behaviours. In this situation, I recommend different feature files for each different type of User Interface. I suggest one feature file for the voice interface behaviours of this component, a different feature file for the touch-enabled interface behaviours, and another for the mouse-based desktop computer interactions, etc. Each feature file would have the exact same set of Behaviour Specifications and step wording. The context - which User Interface type the feature file refers to - conveys the understanding of the User Interface implementation mechanism, and how, for example, a button’s click action is triggered.

Again, there is no need in the Behaviour Specification to specify implementation details, and if those details are desired, they should be available in the test run output.

If a team would like to document the fact that all these different User Interface interaction mechanisms are available, then fantastic - but that belongs in a User Interface Guide and not as Behaviour Specifications in a feature file!

Understand Technical Tool Constraints

At the end of the day, the developers and/or testers must be able to successfully implement the Behaviour Specifications as automated tests. If they cannot do so because of reasons related to the technical tool that enables the execution of the specifications (e.g. Cucumber, SpecFlow, etc), then either the style of writing the Behaviour Specifications must be altered or the tool must be changed.

There is a constraint with the tool Cucumber that may prevent a team from following some of the recommendations in this article and The Behaviour Specification Handbook [1]. With Cucumber, each step within a set of feature files that are executed together is unique and only can have one corresponding implementation. Because of this, when using Cucumber, there cannot be Behaviour Specification steps with the exact same wording in more than one feature file, and with different implementations that are specific to the context of their software component. For example, it would not be possible to implement the suggested approach of having different feature files for the different User Interface mechanisms of voice or touch. With Cucumber, in order to make the wording of each step unique, there would be no choice but to embed specific technical implementation details into the wording of each step, so that each step can be precisely implemented as necessary. The other alternative would be to execute the feature files separately - but for the example of voice and touch interfaces, that does not seem like a good approach since they all relate to the same logical grouping of functionality.

This constraint is an intentional design decision made by the Cucumber development team, with the idea that it forces the usage of a ubiquitous language and fosters better reusability and clarity of steps [16]. However, defining more precise steps (in most cases, by specifying implementation details) with the goal that it increases pressure to grow a business's ubiquitous language (more than likely with technical terminology) is not, in my opinion, a desirable goal or outcome.

The approach I recommend both embraces and highlights the context of a software component and feature file as the key to understanding each behaviour, and also strongly emphasises the usage of an accepted everyday business language. This also probably is another reason that many common behaviour examples follow practices that I do not recommend, because the idea of context is either not well understood or not embraced.

The Cucumber Backgrounder [17] interestingly goes so far as to say that relating the step definition files to specific feature files is considered an anti-pattern - although quickly and wisely advises that you may depart from that advice when you are experienced enough to evaluate the trade-offs. Over the years I have given this a great amount of thought, attention and real-life practise, and with the guidelines in this article and The Behaviour Specification Handbook [1], I have witnessed many more benefits than negatives. Unfortunately, Cucumber does not appear to be the right tool for the job. There are however other suitable tools available.

In the world of Ruby, Turnip [6] partly was created as an alternative to Cucumber because of this same desire in the community to have step implementations scoped to behaviours [18].

In the .NET world, the open source tool SpecFlow [9] provides the test automation developer with the option of reusing existing step implementations where useful and desired, and also the ability to have different implementations for the same step wording in different feature files.

Conclusion

Using the recommendations in this article and in The Behaviour Specification Handbook [1], over the last five years I have practised Behaviour-Driven Development at enterprise-scale, and successfully written and implemented Behaviour Specifications that:

  • Are executed by an enabling technical tool - such as SpecFlow [9];
  • Are logically grouped into software components;
  • Are written in common, everyday, accepted business language - not technical terminology - that the entire team and business stakeholders can understand; and

Do not include internal implementation details - making them robust and resilient over time to technical design changes in the code, as well as helping to make them readable.

With this approach:

  • Communication and understanding within the team increases;
  • The software development lifecycle can be optimised;
  • The Testing Pyramid can be maintained;
  • Test case duplication can be minimised; and
  • Total cost of ownership for software development and delivery can be reduced.

Further details, explanations and tips are available at the free online resource, The Behaviour Specification Handbook [1].

References and Links

[0] Craven, Adam. Channel Adam Software Development Zone. Accessed August 2015.
https://DevZone.ChannelAdam.com

[1] Craven, Adam. "The Behaviour Specification Handbook." Channel Adam Software Development Zone. Accessed August 2015.
https://DevZone.ChannelAdam.com/library/bdd-behaviour-specification-handbook

[2] Wacker, Mike. "Just Say No to More End-to-End Tests." Google Testing Blog. April 22, 2015. Accessed August 2015.
http://googletesting.blogspot.co.uk/2015/04/just-say-no-to-more-end-to-end-tests.html

[3] International Software Testing Qualifications Board (ISTQB). Accessed August 2015.
http://www.istqb.org

[4] "Equivalence Partitioning." Wikipedia. Accessed August 2015. https://en.wikipedia.org/wiki/Equivalence_partitioning

[5] Cucumber Open Source. Cucumber/Gherkin Wiki. Accessed August 2015. https://github.com/cucumber/gherkin/wiki

[6] Nicklas, Jonas. Turnip. Accessed August 2015.
https://github.com/jnicklas/turnip

[7] Codegram Technologies. Spinach. Accessed August 2015.
https://github.com/codegram/spinach

[8] Cucumber Open Source. Cucumber. Accessed August 2015.
https://cukes.info

[9] TechTalk. SpecFlow. Accessed August 2015.
http://www.specflow.org

[10] jbehave.org. JBehave. Accessed August 2015.
http://jbehave.org

[11] Behat.org. Behat. Accessed August 2015.
http://behat.org

[12] ThoughtWorks, Inc. "Specifications". Gauge Documentation. Accessed August 2015.
http://getgauge.io/documentation/user/current/specifications/README.html

[13] North, Dan. "Introducing BDD." Dan North & Associates. September 20, 2006. Accessed August 2015.
http://dannorth.net/introducing-bdd

[14] Agile Alliance. "Given - When - Then." Guide to Agile Practices. Accessed August 2015.
http://guide.agilealliance.org/guide/gwt.html

[15] Agile Alliance. "Acceptance Testing." Guide to Agile Practices. Accessed August 2015.
http://guide.agilealliance.org/guide/acceptance.html

[16] "Scoped step definitions in Cucumber-JVM." Google Groups Cukes Forum. Accessed August 2015.
https://groups.google.com/d/topic/cukes/VfeQrfxNH8c/discussion

[17] Byrne, James B. "Cucumber Backgrounder." GitHub Cucumber Wiki. Accessed August 2015.
https://github.com/cucumber/cucumber/wiki/Cucumber-Backgrounder

[18] Nicklas, Jonas. "Solving Cucumber's Problems." Elabs. November 17, 2011. Accessed August 2015.
http://www.elabs.se/blog/30-solving-cucumber-s-problems


Related software testing articles

Behavior Driven Database Development (BDDD)

TDD - FDD - BDD... Why not PDD?

Acceptance Test Driven Development (ATDD) Explained


Click here to view the complete list of archived articles

This article was originally published in the Fall 2015 issue of Methods & Tools

Methods & Tools
is supported by


Testmatick.com

Software Testing
Magazine


The Scrum Expert