6 Steps to Glory: How to build better products using BDD

Norman Steger
comsystoreply
Published in
14 min readApr 3, 2024

Building real-world apps can be hard. Making your end users love using them can be even harder. Behaviour Driven Development (BDD) is but one discipline that can help you get there — if you do it right.

But what is Behaviour Driven Development anyway? You might have heard that term once or twice in the past. It is often used in the same context with terms like “Cucumber” or “Gherkin Syntax”, or “Automated Acceptance Tests”.

But what if I told you that BDD is much more than just that?

You take the blue pill, the story ends. You wake up in your bed and believe whatever you want to. You take the red pill, you stay in Wonderland, and I show you how deep the rabbit hole goes.

When we look at how Wikipedia defines Behaviour Driven Development we stumble upon this very interesting statement:

In software engineering, behaviour-driven development (BDD) is an agile software development process that encourages collaboration among developers, quality assurance testers, and customer representatives in a software project.

It encourages teams to use conversation and concrete examples to formalize a shared understanding of how the application should behave.

For a long time, I didn’t follow these principles myself. I started out by building web applications based on my own experiences and expectations, without collaborating with any relevant party right from the start. Doing so, I might have had the best intentions in mind, but eventually I ended up crashing and burning, because I just wasn’t able to provide value to end users. The reasons for that were various:

  • I did not understand who the users were and what their problem was
  • Users didn’t understand the product I built for them or how it would help them to solve their problem
  • It didn’t solve any problem for them at all
  • The product was barely usable (poor user experience)

With those being just a few examples: I don’t want you to make the same mistakes I did. So listen up, chap! Here are my six steps to glory on how BDD can help you build a project your users will love:

Disclaimer

I will be listing a couple of tools and methodologies in the steps below. Those are merely examples that I have found to be helpful in the past. Some of these tools and services may require you to create an account or a subscription to use them.

Please note that neither myself or Comsysto Reply is sponsored by any of these companies. There are many other options out there that you may use as an alternative — feel free to do your own research and let me know what your tools of choice are!

Step 1: Design Thinking

Every solution starts with a problem. And it is vital for you to understand that problem before you attempt to come up with any proposal for a solution. But as the late Steve Jobs used to say:

“Great things in business are never done by one person. They’re done by a team of people.”

This is why it is not only vital to you but also to every other party involved in designing and building a product, to create a common understanding about the user, their problem and how to solve it. Here’s the concept in a nutshell:

Design Thinking helps you to:

  1. understand who your users are
  2. facilitate a common understanding of the problem they might have
  3. generate ideas and prototypes to tackle the problem
  4. put them to the test by confronting your end users with your ideas and prototypes
  5. repeat that process until you end up with something useful for the user

Following this principle from the very beginning of product development will be a corner stone to build something that your users will truly love to use. And at the end of the day that is (or should be) also at the heart of your businesses interests.

Step 2: Develop a Common Language

Photo by Nigel Tadyanehondo on Unsplash

Did you ever join a new company or team and then ended up being confused about the terminology that is being thrown around in conversations about the product?

That might be due to an established language that has developed among the parties involved. Which is a good thing, but: For the same reasons as having a common understanding about the product and solution, it is also crucial to have a common understanding about the terminology that is being used across teams and stakeholders that work on the same product.

Why is that important?

There are a couple of examples that might fall in the same “category” of terminology, which — if taken individually — do differ significantly in the context of the product. Let’s say we want to build a multichannel E-Commerce platform. A common example for related terminology that might be used are User Personas.

Here are a few examples:

  • B2C customers
  • B2B customers
  • Account Managers
  • Distribution Managers

Depending on their role, users might have completely different motivations and goals when interacting with your product. You need to keep that in mind when you start talking about your users.

Another common example would be App pages, like:

  • Home- or Landing Page
  • Product Detail View (PDV)
  • Product List View (PLV)
  • etc.

Abbreviations in this example might be completely customary to your organisation. It is important though for everyone to be on the same page (pun intended 😛), once such terms or abbreviation are being used. Does everyone have the same page in mind, when you’re talking about the “Landing Page”? Or are there different understandings? Make sure there aren’t by defining and documenting such terms.

Step 3: User Flow Scenarios

Understanding how users interact with your product in order to reach a specific goal is essential to providing value.

Using a common language to describe user flows will not only help you to understand them but also to make them reproducable. It shouldn’t matter if you are a software engineer, UX designer, QA tester or product owner. Looking at the definition of a feature, everyone of you should be able to understand and recreate the behaviour of how a user is supposed to interact with a specific feature in your product.

Using “Gherkin” Syntax would be one example of how to achieve that:

GIVEN I am on http://www.my-app.de/home
AND I use a smartphone device
AND my browser language is english
WHEN I click on the „Get started“ button
THEN I see a list of all available product categories

Prefer to be explicit rather than implicit when writing a scenario using Gherkin syntax. Even at this stage, little details can make quite a difference in ending up with the same result in the end:

Still don’t like Gherkin? Make up your own Syntax! Whatever works for you and your team - just make sure it follows the same principles to establish a reproducible process description:

Scenario:
DEVICE desktop ultra wide screen
NAVIGATE https://www.my-app.de/products/tv
CLICK product tile „Philips TV XSHL990“
Expectation:
NAVIGATE TO https://www.my-app.de/products/philips/tvs/philips-tv-smartxshl990

Also — and this is an important one — user flow scenarios should derive from asking your users how they expect the product to work. Not the business. Now, you might not want to hear this, but: If you are a software engineer, UX designer, QA tester or product owner, working on the product, you might have an expectation of how a user should interact with a feature, but more often than not you’re probably not a user within the target group yourself. You’re part of the business side.

The people that are actually suppose to use your product and generate (or save) revenue on the other hand — those are users — the ones you should be worried about when describing the behaviour of interacting with your product.

Yes. All of these. User flow scenarios help you to define them and make them reproducible. What you want to eventually end up with is something that you can use as a form of acceptance criteria in your user stories:

Step 4: Testing Behaviour in Unit Tests

Did you know that you can use your user flow scenarios and transfer them directly into your UI unit tests? At least some of them you can. And it will also help you foster the right mindset for test-driven development when approaching the implementation of features. Isn’t that great?

Here’s an example of what test case definitions could look like in your code (using TypeScript syntax with Jest):

describe('Product List', () => {

it('loads a list of product categories on init', () => {});

it('loads a list of products when clicking on a product category', () => {});

it('shows an error notification if product categories can not be loaded', () => {});

it('shows an error notification if product list can not be loaded', () => {});
});

Most commonly you will find two different types of assertions you can cover within such test cases - those that have a direct impact on what the user sees vs those that verify the behaviour under the hood:

showAllTvsButton.click();

// non-visual: expect API requests to have been fired
expect(ProductApi.getProductList).toHaveBeenCalledTimes(1);
expect(ProductApi.getProductList).toHaveBeenCalledWith({ categoryId: '123-tv'});

// visual: expect the view to have changed
expect(listContainer.querySelector('.product-tile')).toHaveLength(5);

Making sure that your unit tests cover what is essential to your user flow scenarios to work is not only a great way of applying BDD principles to the lowest level of your development process, it also serves as a valuable piece of documentation, as other developers can easily read from your test cases what your code should be doing and thus how it is supposed to provide value to your feature unit.

Step 5: Run Automated Acceptance Tests

Not every possible flaw within a user journey implementation can be covered by UI unit tests. The nitty-gritty of short-comings in a user journey often happen on a higher level — in between the interaction of various components, pages and app states. To cover these gaps, integration and end-to-end tests are commonly used to validate that all parts of a system work together seamlessly and by that, ensure the desired outcome of a user flow scenario across different steps of the journey.

A couple of examples for popular tools to automate such tests in the browser are:

As the line between what is considered to be an integration test and what is considered to be an E2E test can become blurry depending on the specific use case, we don’t want to get too deep into their separation in the context of BDD.

  • Want to call real server endpoints from your browser UI to cover every possible flaw in one go? Go for it!
  • Want to isolate the UI from the server to make sure the front-end does what it’s supposed to do independent of the back-end? That might also be a valid approach

No matter what testing strategy you choose, the one thing you should always care most about is that your acceptance criteria is being met and your test setup provides everything that is necessary to meet that requirement. So for the sake of simplicity, we will refer to both of these approaches simply as automated acceptance tests (AATs).

Here’s a practical example of an AAT specification, using the cypress-cucumber-preprocessor plugin in Cypress:

Feature: Product List 

GIVEN I am on http://www.my-app.de/products
AND I am using a smartphone device
AND my selected language is english

Scenario: Poroduct List loads succesfully

WHEN I click on the "Get started" button
THEN I see a list of all available product categories

Scenario: Product List can not be loaded

WHEN I click on the "Get started" button
THEN I see an error notification telling me that hte product list can not be loaded

This translates to the following test implementation code:

import { When, Then, Given } from "@badeball/cypress-cucumber-preprocessor";

Given('I am on ""', (url) => {
cy.visit(url);
});

And('I am using a smartphone device', () => {
cy.viewport({ widht: 320, height: 480 });
});

And('my selected language is english', () => {
cy.get('.languages > .english-language').click();
});

Then('I see a list of all available product categories', () => {
cy.get('.product-category').should((list) => {
expect(list).to.have.length(6);
expect(list[0]).to.have.text('Best Sellers');
expect(list[1]).to.have.text('Women');
expect(list[0]).to.have.text('Men');
//etc...
});
});

Bonus: These blocks can easily be reused across your test cases!

If you’ve already followed all the previous steps, the good news is: You’re already half-way there!

Since user flow scenarios are supposed to be a direct reflection of the acceptance criteria from the user’s perspective, you can easily transfer them into your AAT test cases. One way to do this is to use Cucumber Plugins to enable you to describe test cases in Gherkin syntax. Such plugins are available for most popular test runners such as Cypress, TestCafé or Playwright. Using them makes requirement loss in translation even less likely. And as a nice side-effect: It makes your test cases transparent even to less technical team members.

Step 6: Measure & Learn

Once you’ve followed all previous BDD principles during your implementation phase, the last piece of the puzzle is to keep an eye on what is happening when real users interact with your product. This will help you to improve the user’s experience along the way and can be achieved on multiple levels:

User Tracking

Photo by Pixabay: https://www.pexels.com/photo/gray-and-black-laptop-computer-265087/

While the term itself has become quite heavily loaded with privacy concerns — for good reasons I may add — the ability of tracking your users behaviour in a live product is still a vital form of direct feedback to foster improvement. The key here is to ensure anonymity and transparency for your end users. Agreeing to collecting user data, be it personalised or not, should always be a choice that you should enable your users to provide explicitly rather than implicitly.

Tools to track your users are various. Some popular examples might be:

But what exactly can you learn from your users using these tools?

  • What pages do you users visit most?
  • How do your users interact with your app / feature?
  • What browser / device / version do they use?
  • What times are your users most active?
  • Where do your users come from?
  • Depending on privacy level:
    What age / gender / etc. are your users?

These are just a few examples that may help you optimize the user experience of your product.

That being said, even if a user agrees, there might be hidden disagreements in the form of active ad blockers that can still prevent you from collecting data.

Always keep that in mind while looking at the numbers. Again — explicitly asking your users to disable their ad blocker in order to improve their user experience in the long run can go a long way in terms of establishing trust.

Error Monitoring

Photo by David Pupăză on Unsplash

Errors that happen within your system may not always be captured by user tracking tools. Everything from an internal server exception, to a corrupted UI state caused by a JavaScript error, or a HTTP exception response due to a crooked validation may occur completely hidden from your watchful eye if you don’t setup sufficient monitoring.

Here are a few choices that can help you catch such issues:

Monitoring errors both on the client and on the server side will eventually help you detect road blocks that your users run into frequently and how to resolve them. Less errors = less friction in the user journey = happier users.

A/B Testing

Photo by Pixabay: https://www.pexels.com/photo/yellow-and-black-2-way-signage-272254/

Testing out new features with real users can be done both in the concept phase (design thinking) as well as in the post-implementation phase. A/B Testing is a common practice to enable only specific users to access new features within your product so you can monitor the impact and improve the experience before rolling it out to everyone in your user base.

Implementing an A/B testing process in your product can be a custom built solution (which is the approach that I’ve mostly taken in the past). However, it is worth mentioning that there are also vendors that are offering out-of-the-box solutions that help you with implementing an A/B test flow and related analytics in your product, such as:

Personally, I couldn’t make any experiences with these tools yet but if you do, please share your thoughts in the comments!

Browser and Device Testing

Photo by Pixabay: https://www.pexels.com/photo/macbook-and-ipad-on-desk-207589/

Users access your product using different types of interfaces. These usually include a various range of different browsers, operating systems, devices and related versions. These differences can come with quirks that affect the experience for your users based on the browser, device and viewport of choice.

Having a process in place to test your product via different interfaces can help you catch compatibilty issues, as well as variations in behaviour and performance. Here again, there are different tools that can help you achieve that without having to keep an armada of different devices at your desk that you have to keep up to date by yourself:

Talk to your users!

Photo by Armin Rimoldi: https://www.pexels.com/photo/serious-young-diverse-men-using-laptop-5553922/

All the tooling in the world will not replace direct user feedback. Observing your users as they interact with your product and having them thinking out loud what they’re experiencing helps you see the application through their eyes.

Of course, verbal feedback is always subject to personal perception, but it also tells your more about how your users feel about using your product than any abstracted set of data can ever do.

And feeling — or the power of emotional attachment to a product or brand, to put it in different words — is a powerful ally in fostering word of mouth marketing.

Conclusion

As you can see there are many ways to apply BDD principles to the development cycle of your product. While any of those steps can be used independently from each other, it’s worth to keep in mind that they are designed to compliment each other so to maximize the benefit you may draw from them.

Also if you’re familiar with agile software development and the principles of the lean startup, you may have noticed that the concepts behind those steps are merely derivations of just that— build, measure, learn, repeat. Always keep this sequence in mind, when in doubt.

Let me know how you apply BDD in your project and what you’ve found worked best for you! Also, if you want to know more about how you can make your product a success, please check out the DELTA framework by Comsysto and reach out to us!

And please make sure to leave a 👏🏻 and share this article with your friends and colleagues 😉

This blogpost is published by Comsysto Reply GmbH

--

--