Behavior-Driven Development – Making TDD more accessible and effective for better Agile analysis

In addition to increasing complexity, the changing dynamics of technology, and meeting the quality expectations of prospective clients, the main underlying challenge in modern IT organizations nowadays is managing the scope and requirements of software projects. The challenge is not just about eliminating uncertainties by defining, elucidating, and locking down requirements as quickly as possible, but also about dealing with this uncertainty in a way that will help organizations progressively discover and deliver an effective solution, not only for the projects, but also for their underlying business goals. Although many project development methodologies have been put in place to counteract these challenges, they have proven to be less than suitable or have even failed. So it is here that BDD can provide the requisite techniques and/or approach to help organizations manage these uncertainties and reduce the associated risks. Nonetheless, BDD has evolved as a strategic necessity to help teams build and deliver more valuable and higher quality software.

Behavior-driven development was actually designed by Dan North in the early to mid 2000s as an easier way to teach and practice Test Driven Development (TDD). TDD is actually a simple but incredibly powerful engineering practice that promotes better-designed code and results in substantially lower defect counts. However, as of today, many project teams have difficulty adopting it and using it effectively. So, Dan observed that implementing a few simple practices (naming unit tests as full sentences and using some declaratory words such as „should“, etc.) can help developers write more focused tests, which in turn helps them write higher quality code more efficiently.

For example, a traditional unit test for an e-commerce application might look like this:

public class PlaceOrderTest extends TestCase
{
public void testLogin() {…}
public void testPlaceOrder() {…}
}

But by using above style, developers often have trouble knowing where to start, or what tests they should write next. So Dan found it easier to think in terms of what the above class should do:

public class BeforePlacingOrder
{
public void should_login_into_registered_account() {…}
public void should_place_an_order() {…}
public void should_make_payment_as_a_separate_transaction() {…}
…
}

So tests that are written in this fashion end up reading more like specifications than unit tests, because they focus more on the behavior of the application, using tests simply as a means to express and verify that behavior. Dan also noted that tests written in this way were much easier to maintain, because their intent was so clear. Further, the impact of this approach was so significant that Dan started referring to what he was doing no longer as “Test Driven Development”, but as “Behavior Driven Development”. Importantly, to promote the idea further, Dan North himself wrote the first dedicated BDD test automation library called JBehave. (more information about the framework is available at the website http://jbehave.org/reference/stable/index.html).

The figure below provides a high-level view of BDD. Initially, BDD practitioners start by identifying business goals and looking for features that will help deliver these goals. Further, collaborating with the end-user/client, they use concrete examples to illustrate these features. Wherever possible, these examples are automated in the form of executable specifications, which both validate the software and provide automatically updated technical and functional documentation. BDD principles are also used at the coding level, where they help developers write code that is of higher quality, better tested, better documented, and easier to use and maintain.

Figure-1: High Level view of flow of activities in BDD.

Figure-1: High Level view of flow of activities in BDD.

The figure-1 outlines the principal activities and subsequent outcomes of Behavior Driven Development and it is important to note that these activities occur repeatedly and continuously throughout the process, because it is not a single linear waterfall-style process, but a sequence of activities that the team will practice for each feature they intend to implement.

BDD – Key Activities Conceptualized

  1. Develop features that deliver business value. As mentioned above, having massive specifications up front do not really work very well, particularly for software projects. So, rather than attempting to finish the requirements once and for all, teams implementing the BDD approach participate in ongoing conversations with end-users and other stakeholders to attain a common understanding of what features they should develop first.Rather than working up front to design a complete solution for the developers to implement, users explain what they need to get out of the system, and how it might help them achieve their objectives. And instead of accepting a list of feature requests from the users without asking any questions, teams should first try to understand the core business goals underlying the project, and recommend only the features that can be demonstrated to support these business goals. This constant focus on delivering business value means that teams can deliver more useful features earlier and with less wasted effort.
  2. Collaborate and work to specify features. BDD is a highly collaborative practice, both between users and the development team, and within the team itself. Business analysts, developers, and testers work together with the end-users to define and specify features, with team members drawing ideas from their individual experience and expertise. This makes the approach highly efficient.Unlike a more traditional approach, when business analysts effectively communicate their understanding of the user requirements to the rest of the team, there is always a very high risk of ambiguity, misinterpretation, and information loss. Developers cannot use their technical expertise to help deliver a technically superior design, and the QA folk do not get the opportunity to comment on the testability of the specifications until the end of the project. On the other hand, when teams practice BDD, team members build up a shared appreciation of users’ needs, as well as a sense of common ownership and engagement in the solution.
  3. Keep a grip on uncertainty situations. A BDD team realizes that they will not know everything up front, no matter how long they spend writing specifications, and the biggest thing slowing them down in a software project is actually understanding what they need to build.
    So, rather than attempting to lock down the specifications at the start of the project, BDD practitioners assume that the requirements, or more precisely the understanding of the requirements, will evolve and change throughout the life of the project. Instead of waiting until the end of the project to see if their assumptions about the business requirements are correct, teams try to get early feedback from users and stakeholders to ensure that they are on track, and accordingly change the approach.
    Very often the most effective way to see if users like a feature, is to build it and show it to them as early as possible. With this in mind, experienced BDD teams prioritize the features that will both deliver value and improve their understanding of what features the users really need, and of how best to build and deliver these features.
  4. Explain features with constructive examples. When a team practicing BDD decides to implement a particular feature, the team works together with end-users to define stories and scenarios of what they expect this feature to deliver. In particular, the user helps to define a set of concrete examples that illustrate the key outcomes of a feature. These examples use the same common business vocabulary, and can be readily understood by end users and by members of the development team. Examples play a key role in BDD, simply because they are an effective way of communicating clear, precise, and unambiguous requirements. Unlike specifications written in natural language, turns out to be a very poor way of communicating requirements because the approach itself opens space for ambiguities, assumptions, and misunderstandings to creep in.Nevertheless, ‚examples‘ serve as an option for exploring and expanding our understanding and expertise. This is because when a user proposes an example of how a feature should behave, project team members will often ask for extra examples to illustrate some exceptional cases, explore edge cases, or clarify some assumptions etc. This practice is very helpful for testers as well, which is why it is so valuable for them to be involved at this stage of the project.
  5. Focus on writing executable specifications. Usually stories and examples form the basis of the specifications that developers rely on to build the system. Nevertheless, they act as both acceptance criteria in determining when a feature is done, and as a guideline to give the development team a clear picture of what they are supposed to build.
    Acceptance criteria give the team a way to objectively judge whether a feature has been implemented correctly or not. However, checking this manually for each new code change would be time-consuming and inefficient. This would also slow down feedback, which would in turn slow down the development process. So, wherever feasible, teams turn these acceptance criteria into automated acceptance tests, or, more precisely, into executable specifications. These automated tests are executed as part of the build process, and run whenever a change is made to the application. In this way, they serve both as acceptance tests, determining which new features are complete, and as regression tests, ensuring that new changes have not broken any existing features.
    Unlike conventional unit or integration tests, or like the automated functional tests that many QA teams are used to, executable specifications are expressed in something close to natural language. They use precisely the examples that the users and development team members proposed and refined earlier on, using exactly the same terms and vocabulary. Executable specifications are about communication as much as they are about validation, and the test reports they generate can be easily understood by everyone involved with the project.
    Importantly, these executable specifications also become a single source of information, providing reference documentation for how features should be implemented. This makes maintaining the requirements much easier.
    As currently observed in many of the conventional projects, if specifications are stored in the form of a Word document or on a Wiki page, then any changes to the requirements will need to be reflected both in the requirements document and also in the acceptance tests and test scripts, which introduces a high risk of inconsistency. But for teams practicing BDD, the requirements and the executable specifications are the same thing. So when the requirements change, the executable specifications are updated accordingly.
  6. Do not write unit-tests, instead formulate low-level specifications. Behavior Driven development does not stop at acceptance tests. BDD also helps developers write higher quality code that is more reliable, more maintainable, and better documented. Developers practicing BDD typically use an “outside-in” approach, i.e., when they implement a feature, they start from the acceptance criteria and work down, building whatever is needed to make those acceptance criteria pass. The acceptance criteria define the expected outcomes and the developer’s job is to write the code that produces these outcomes.
    This is a very efficient, focused way of working. Just as no feature is implemented unless it contributes to an identified business goal, no code is written unless it contributes to making an acceptance test pass, and therefore to implementing a feature. But it does not stop there, either. Before a BDD developer writes any code, the individual developer will reason about what this code should actually do, and express this in the form of a low-level executable specification. The developer will not even think in terms of writing unit tests for a particular class, but will consider writing technical specifications describing how the application should behave. For example, how it should respond to certain inputs or what it should do in a given situation.
    These executable specifications are a little like conventional unit tests, but written in a way that both communicates the intent of the code, and gives a worked example of how the code should be used. So writing low-level executable specifications in this fashion is analogous to writing detailed design documentation, with lots of examples.
    At a more technical level, this approach encourages a clean, modular design with well-defined interactions (or APIs, if you prefer a more technical term) between the modules. It also results in code that is reliable, accurate, and extremely well tested. These low-level executable specifications also facilitate code maintenance. When a developer adds a new feature, or changes an existing one, some of the existing executable specifications may fail. When this happens, it can mean one of two things. If the broken specification is still valid, then the developer has introduced a bug and it needs fixing. If the requirements have changed and the specification is no longer valid, this specification can be updated to reflect the new requirements, or deleted if it is no longer applicable. Thinking in terms of executable specifications rather than conventional unit tests makes this process a great deal easier.
  7. Deliver the complete gamut of documentation. The reports produced as part of BDD activities are no longer simply technical reports meant for developers, but effectively become a form of product documentation for the whole team, expressed in the same common vocabulary familiar to the end-users. This documentation is always upto date and requires minimal or no maintenance. It can be automatically generated from the latest version of the application. Further, for web-based applications, this sort of documentation also includes screen-shots of the application for each feature.
    Teams organize this documentation so that it is easy to read and easy to use for everyone involved in the project. It is extremely helpful because developers can consult it to see how existing features work. Likewise, testers and business analysts can see how the features they specified have been implemented. In addition, product owners and project managers can use summary views to ascertain the current state of the project, view progress, and decide which features can be released into production. Just as automated acceptance criteria provide great documentation support for the whole team, low-level executable specifications also provide excellent technical documentation for other developers. This documentation is always upto date and cheap to maintain, contains working code samples, and expresses the intent behind each specification.

So with all the key activities in place, project teams can add value to the three main aspects of software development, i.e., defining, coding, and testing to build the right software with which the users can achieve their business goals with the utmost ease and sophistication.

Narayana Maruvada

Narayana Maruvada is a computer science and engineering graduate, currently working as System Analyst – QA with ValueLabs. He has more than 7 years of experience working on both developing and testing web-based applications. A major area of work and...
Read more about Narayana Maruvada