Behavior Driven Development in Python with Lettuce

This is the next post in our series of Test Driven Development. We have already talked about the following:

  1. An intro tutorial on TDD with python/django.

  2. An Introduction to Unit Testing django Views and Models

  3. Testing of django models and forms

  4. Functional Testing with Selenium WebDriver in Python

Lets talk about Behavior Driven Development in this post. In software development we usually start with a specification, coming from the manager/client/customer. The specification contains the user stories that define the behavior of the application. Given a behavior is where we start in, it is always good if we modularize our approach and integrate that in our coding practise. BDD or Acceptance Test has evolved out of established agile practices and is designed to make them more accessible and effective for teams new to agile software delivery.

It gives you the language (Gherkin) and tools (Lettuce, Freshen, Behave etc) that enable you, the developer, to translate these user stories into features and scenarios that are easy to understand by a non-technical person (eg. customers, stakeholders etc.) This gives you the option of training a person to write these specifications, leaving you (or a tester) the job of implementing the tests.

Most people fail at getting TDD correct. Due to some of the inherent problems with TDD and the huge gray area without sufficient guidelines on what and how much to test, people like Dan North came out with the idea of BDD a.k.a. Behavior Driven Development. The idea behind BDD is pretty simple. The idea is to provide a common tool and guideline to both the business and development folks. This tool/guideline would allow the business analysts and SME’s to write the business requirements (in a specified format) using english-like language.

BDD can help facilitate the communication with non-technical members of the team. So using a DSL w/in features/steps keeps the "code lingo" out of the communication. They become testable documents; with the down side of agreeing on the DSL and the mapping of the DSL to the system under test.

How is BDD/TDD same and different?

As already know, the only difference between any two general programming languages/paradigms are the things that their syntaxes encourage and discourage. “Behaviour” is a more useful word than “test”. There is no such thing, strictly speaking, as a "BDD test". BDD is a philosophy that suggests how you can best interact and collaborate with stakeholders to complete a complex project. It doesn't directly make any prescriptions for the best way to write tests. In other words, you'll probably still have all the usual kinds of tests (including acceptance tests) under a BDD-philosophy project. BDD is about writing specs(a.k.a test) first from a user point of view rather than the programmer.

How do we proceed?

It roughly goes like this:

  1. Write spec for a feature(or a scenario), and see it fail.

  2. Think how to make a view, write a spec, see it fail.

  3. Now write code for view (specs pass, now you have non functioning HTML page)

  4. Think how make it function, write spec for controllers, see it fail.

  5. Write code for controller (pass, now you have functioning HTML page without data)

  6. Think how to manage data, write spec for model, see it fail. [programmer view]

  7. Write code for model and see it pass.

  8. Now a feature spec will pass.

  9. Continue to next feature.

The steps may be omitted or expanded per case.

Given that the core concepts of BDD and TDD are not different, it doesn't matter to do unittest in BDD style. I think the whole thing about BDD is to encourage the programmer to think of writing a spec first, rather than "implement a test after the code".

A simple example of a feature put in a gherkin syntax can be the following:

Feature: Run or Riot In order to test my bot's strength, As a bot developer, I want to program my bot's decision making. Scenario: Boring Opponent Given the bots network has been trained When attacked by PoorOpponent Then the bot should kill the opponent Scenario: Legendary opponent Given the bot's network has been trained When attacked by StrongOpponent Then the bot should bow down in respect!

Lets look how to do it in django with a simple example: We start with a library called lettuce, as described above, it brings all the goodness and freshness that is needed for BDD. The installation can be easily done using: pip install lettuce. I assume you have a basic django app setup, and the folder structure required for it would be as follows:

/home/user/projects/mymath | tests | features - fibo.feature - steps.py

Lets define a problem statement for ourselves, from a given set of numbers, if the number is greater than 10, we will calculate its corresponding fibonacci value.

Now the above problem statement can be translated into gherkin syntax as follows:

Feature: Compute fibonacci In order to play with Lettuce Lets implement fibonacci Scenario Outline: Fibonacci Testing Given I have the number And given number is less than 10 When I compute its fibonacci Then I see the number Examples: | test_number | test_number_fibo | | 0 | 0 | | 1 | 1 | | 6 | 8 |

As is clear from the above explanation, the gherkin syntax tries enforce the following feature:

  1. pick a number from the examples table.

  2. Check if it is less than 10

  3. Calculate its fibonacci, and assert.

Now go to the app/test directory in your terminal and run lettuce and you should see a message that asks you to implement a step file. You are free to choose any name for the file. Lettuce will recursively try and find it. You can start from the template generated, and eventually adding the if, and a fibonacci function your file would look something like below:

from lettuce import step, world

    @step(u'Given I have the number (\d+)')
    def given_i_have_the_number_group1(step, number):
          world.number = int(number)

    @step(u'When I compute its fibo')
    def when_i_compute_its_fibo(step):
          world.number = fibo(world.number)

    @step(u'And given number is less than 10')
    def and_test_number_is_less_than_10(step):
        if world.number > 10:
            return False
    return True

    @step(u'Then I see the number (\d+)')
    def then_i_see_the_number_group1(step, expected):
         expected = int(expected)
         assert world.number == expected, \
             "Got %d" % world.number

    def fibo(n):
    # a recursive function that tries and calculates the     fibonacci of a number 
        if n == 0 or n == 1:
            return n
        else:
        return fibo(n-1) + fibo(n-2)

Now when you would run the command lettuce you can see the tests failing and passing depending upon the numbers you have put in the table. Fibonacci is a simple example, but good enough to demonstrate different features. Hope the post was helpful.


Comments

blog comments powered by Disqus