Behavior Driven Development in Python with Lettuce
Posted by fRui Apps | Filed under python, behavior-driven-development, test-driven-development, django
This is the next post in our series of Test Driven Development. We have already talked about the following:
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:
Write spec for a feature(or a scenario), and see it fail.
Think how to make a view, write a spec, see it fail.
Now write code for view (specs pass, now you have non functioning HTML page)
Think how make it function, write spec for controllers, see it fail.
Write code for controller (pass, now you have functioning HTML page without data)
Think how to manage data, write spec for model, see it fail. [programmer view]
Write code for model and see it pass.
Now a feature spec will pass.
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
As is clear from the above explanation, the gherkin syntax tries enforce the following feature:
pick a number from the examples table.
Check if it is less than 10
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.