BDD rather than TDD: Result-Oriented Testing
Tags: bdd, tdd, testing
Published: 2019-01-04 20:15:14

---
title: BDD rather than TDD: Result-Oriented Testing
published: true
description: 
cover_image: https://media0.giphy.com/media/nFjDu1LjEADh6/200.webp?cid=3640f6095c2f9a4f65746f666b2d3571
tags: bdd, tdd, testing
---

### **TDD doesn't click, give me the solution**

I've talked before about TDD (Test Driven Development), and how you **end up testing your framework instead of the logic** of your app.

In case you haven't read the previous topic, you can find it here: https://dev.to/0xrumple/when-tdd-doesnt-click-something-else-should-click-harder-2h9h

### **BDD (Behavior Driven Development) to the rescue**

This time, I decided I will do testing no matter what, so I chose an approach that doesn't focus on mocks, fakes, interfaces, dependency injection... but it's all about checking the outcomes, and that's what BDD is for: **BEHAVIOR**.


#### Did I try it?

Yes, I didn't allow myself to write this post until we (me and my creative friend @alhakem) finished this dormitory management system (where students can search/reserve rooms and managers can approve/reject reservations and manage their dorms):

![demo](https://github.com/coretabs/dorm-portal/raw/master/demo.png)

Here is the whole project, "features" folder contain the tests: https://github.com/coretabs/dorm-portal
Feel free to PR or create issues.

#### Fun facts

I worked on a bit complex filtering engine (where there are multiCheckbox/singleCheckbox/integer filtering criteria), and with BDD I finished it in about **one week**... all tests green, woohoo!

The **whole project** was done in about **two months**, and... all green 😉

### **How can I do BDD?**

It's really simple:

#### 1. Write an acceptance criteria (scenario)

The common way is using Gherkin language, now don't get scared cuz it's really intuitive, just look at this sample (should be **self explanatory**):

```
Feature: Reservation

    Scenario: As a student
              I want to make reservations
              So that I get a room to stay in

        Given we have 2 dormitories (and 1 room each available to reserve)
        Given two students who reserved nothing

        When create a reservation
        Then quota of the room should decrease
```


#### 2. Implement the steps

Depending on your language/framework of choice this might differ; I'm using django (with python of course), and there is a great way of doing it using **behave-django package**.

Here are the above mentioned acceptance criteria implemented:

```python
from behave import given, when, then
from api.engine.models import RoomCharacteristics
from features.steps.factory import (create_alfam_dovec_with_4_rooms,
                                    create_student,
                                    create_reservation)


@given('we have 2 dormitories (and 1 room each available to reserve)')
def arrange(context):
    create_alfam_dovec_with_4_rooms(context)


@given('two students who reserved nothing')
def arrange(context):
    context.user1 = create_student(context, 'Owen')
    context.user2 = create_student(context, 'Tia')


@when('create a reservation')
def act(context):
    context.previous_quota = context.room1.allowed_quota
    context.reservation1 = create_reservation(context.room1, context.user1)


@then('quota of the room should decrease')
def test(context):
    context.room1 = RoomCharacteristics.objects.get(pk=context.room1.id)
    assert context.room1.allowed_quota == context.previous_quota - 1
```

#### 3. Make them green

Now comes the part of writing your actual code, and the goal is to pass that quota test.


```python
from django.db import (models as django_models, transaction)
from .exceptions import NoEnoughQuotaException


class Dormitory(django_models.Model):
    name = django_models.CharField(max_length=60)


class RoomCharacteristics(django_models.Model):
    allowed_quota = django_models.PositiveIntegerField(default=0)

    dormitory = django_models.ForeignKey(
        Dormitory, related_name='room_characteristics', on_delete=django_models.CASCADE)

    def decrease_quota(self):
        if self.allowed_quota == 0:
            raise NoEnoughQuotaException()

        self.allowed_quota -= 1


class Reservation(django_models.Model):
    user = django_models.ForeignKey(
        User, related_name='reservations', on_delete=django_models.CASCADE)

    room_characteristics = django_models.ForeignKey(
        RoomCharacteristics, related_name='reservations', on_delete=django_models.CASCADE)

    @classmethod
    def create(cls, *args, **kwargs):
        room_characteristics = kwargs['room_characteristics']
        result = cls(*args, **kwargs)

        with transaction.atomic():
            room_characteristics.decrease_quota()
            result.save()
            room_characteristics.save()

        return result
```


### **Seriously?! That sounds tedious!**

Nah! believe me, cuz I worked on other projects where adding features or changing one simple thing might break many other parts in your project, it literally becomes like:

<img src="https://media.giphy.com/media/11ZSwQNWba4YF2/source.gif" alt="drawing" width="100" height="150"/>
*image source: giphy*

You will realize that once you add X feature and the customers come to you shouting: "registration doesn't work"... **IT SUCKS TO CODE WITHOUT TESTING**.


### **When to do BDD vs TDD?**

![doing-bdd-tdd](https://thepracticaldev.s3.amazonaws.com/i/0ce0eu6mss3w7shjdkng.png)

#### **References**

1. Gherkin language: https://docs.cucumber.io/gherkin/reference/
2. BDD course on Pluralsight: https://www.pluralsight.com/courses/pragmatic-bdd-dotnet
3. Different types of testing explained: https://dev.to/thejessleigh/different-types-of-testing-explained-1ljo