# introduction¶

Lettuce is an extremely useful and charming tool for BDD (Behavior Driven Development). It can execute plain-text functional descriptions as automated tests for Python projects, just as Cucumber does for Ruby.

Lettuce makes the development and testing process really easy, scalable, readable and - what is best - it allows someone who doesn’t program to describe the behavior of a certain system, without imagining those descriptions will automatically test the system during its development.

## get lettuce¶

Make sure you’ve got Python installed and then run from the terminal:

```
user@machine:~$ [sudo] pip install lettuce
```

## define a problem¶

Let’s choose a problem to lettuce:
**Given a number, what is its factorial?**

Note

The factorial of a positive integer n, denoted by n!, is the product of all positive integers less than or equal to n. The factorial of 0 is 1

## project structure¶

Build the directory tree bellow such as the files `zero.feature` and `steps.py` are empty.

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

## lettuce it!¶

Lets begin to describe and solve our problem...

### first round¶

#### [a] describe behaviour¶

Start describing the expected behaviour of factorial in `zero.feature` using English:

```
Feature: Compute factorial
In order to play with Lettuce
As beginners
We'll implement factorial
Scenario: Factorial of 0
Given I have the number 0
When I compute its factorial
Then I see the number 1
```

Note

zero.feature must be inside features directory and its extension must be .feature. However, you’re free to choose its name.

#### [b] define steps in python¶

Now let’s define the steps of the scenario, so Lettuce can
understand the behaviour description. Create the `steps.py` file which will contain
python code describing the steps.

Python:

```
from lettuce import *
@step('I have the number (\d+)')
def have_the_number(step, number):
world.number = int(number)
@step('I compute its factorial')
def compute_its_factorial(step):
world.number = factorial(world.number)
@step('I see the number (\d+)')
def check_number(step, expected):
expected = int(expected)
assert world.number == expected, \
"Got %d" % world.number
def factorial(number):
return -1
```

Note

`steps.py` must be inside features directory, but the names doesn’t
need to be `steps.py` it can be any python file with a `.py` extension.
Lettuce will look for python files recursively within features
dir.

Ideally, factorial will be defined somewhere else. However, as this is just a first example, we’ll implement it inside steps.py, so you get the idea of how to use Lettuce.

**Notice that, until now, we haven’t defined the factorial function (it’s returning -1).**

#### [c] run and watch it fail¶

Go to the tests directory and run from the terminal:

```
user@machine:~/projects/mymath/tests$ lettuce
```

As you haven’t implemented factorial, it is no surprise the behavior won’t be reached:

Our only scenario failed :( Let’s solve it...

#### [d] write code to make it pass¶

Well, by definition, we know that the factorial of 0 is 1. As our only feature is this... we could force factorial to return 1.

```
from lettuce import *
@step('I have the number (\d+)')
def have_the_number(step, number):
world.number = int(number)
@step('I compute its factorial')
def compute_its_factorial(step):
world.number = factorial(world.number)
@step('I see the number (\d+)')
def check_number(step, expected):
expected = int(expected)
assert world.number == expected, \
"Got %d" % world.number
def factorial(number):
return 1
```

#### [e] run again and watch it pass¶

Again, run from the terminal:

```
user@machine:~/projects/mymath/tests$ lettuce
```

And you’ll be happy to see your factorial implementation passed all the behaviours expected:

Great! :)

However, one test is not enough for checking the quality of our solution... So let’s lettuce it again!

### second round¶

Let’s provide more tests so our problem is better described, and so we provide a more accurate implementation of factorial:

#### [a] describe behaviour¶

Let’s provide two new scenarios, for numbers 1 and 2:

```
Feature: Compute factorial
In order to play with Lettuce
As beginners
We'll implement factorial
Scenario: Factorial of 0
Given I have the number 0
When I compute its factorial
Then I see the number 1
Scenario: Factorial of 1
Given I have the number 1
When I compute its factorial
Then I see the number 1
Scenario: Factorial of 2
Given I have the number 2
When I compute its factorial
Then I see the number 2
```

#### [b] define steps in python¶

As we haven’t changed the definition, no need to make changes on this step.

#### [c] run and watch it fail¶

```
user@machine:~/projects/mymath/tests$ lettuce
```

When running Lettuce we realize that our previous implementation of factorial works fine both for 0 and for 1, but not for 2 - it fails. :(

#### [d] write code to make it pass¶

Let’s provide a solution so we get the right factorial for all scenarios, specially for number 2:

```
from lettuce import *
@step('I have the number (\d+)')
def have_the_number(step, number):
world.number = int(number)
@step('I compute its factorial')
def compute_its_factorial(step):
world.number = factorial(world.number)
@step('I see the number (\d+)')
def check_number(step, expected):
expected = int(expected)
assert world.number == expected, \
"Got %d" % world.number
def factorial(number):
number = int(number)
if (number == 0) or (number == 1):
return 1
else:
return number
```

#### [e] run again and watch it pass¶

```
user@machine:~/projects/mymath/tests$ lettuce
```

Great! Three scenarios described and they are alright!

### third round¶

Let’s provide more tests so our problem is better described and we get new errors so we’ll be able to solve them.

#### [a] describe behaviour¶

```
Feature: Compute factorial
In order to play with Lettuce
As beginners
We'll implement factorial
Scenario: Factorial of 0
Given I have the number 0
When I compute its factorial
Then I see the number 1
Scenario: Factorial of 1
Given I have the number 1
When I compute its factorial
Then I see the number 1
Scenario: Factorial of 2
Given I have the number 2
When I compute its factorial
Then I see the number 2
Scenario: Factorial of 3
Given I have the number 3
When I compute its factorial
Then I see the number 6
Scenario: Factorial of 4
Given I have the number 4
When I compute its factorial
Then I see the number 24
```

#### [b] define steps in python¶

As we haven’t changed the definition, no need to make changes on this step.

#### [c] run and watch it fail¶

```
user@machine:~/projects/mymath/tests$ lettuce
```

#### [d] write code to make it pass¶

```
from lettuce import *
@step('I have the number (\d+)')
def have_the_number(step, number):
world.number = int(number)
@step('I compute its factorial')
def compute_its_factorial(step):
world.number = factorial(world.number)
@step('I see the number (\d+)')
def check_number(step, expected):
expected = int(expected)
assert world.number == expected, \
"Got %d" % world.number
def factorial(number):
number = int(number)
if (number == 0) or (number == 1):
return 1
else:
return number*factorial(number-1)
```

#### [e] run again and watch it pass¶

```
user@machine:~/projects/mymath/tests$ lettuce
```

### forth round¶

All steps should be repeated as long as you can keep doing them - the quality of your software depends on these.

## Syntactic sugar¶

Available for versions > 0.2.19

### Steps sentence can now be given by function name or doc.¶

To take a step sentence from function name or doc, just decorate it with “@step” without argument.

These two steps below, are identicals than the example above.

```
from lettuce import *
@step
def have_the_number(step, number):
'I have the number (\d+)'
world.number = int(number)
@step
def i_compute_its_factorial(step):
world.number = factorial(world.number)
```

### Steps can be grouped in class decorated with “@steps”¶

```
from lettuce import world, steps
@steps
class FactorialSteps(object):
"""Methods in exclude or starting with _ will not be considered as step"""
exclude = ['set_number', 'get_number']
def __init__(self, environs):
self.environs = environs
def set_number(self, value):
self.environs.number = int(value)
def get_number(self):
return self.environs.number
def _assert_number_is(self, expected, msg="Got %d"):
number = self.get_number()
assert number == expected, msg % number
def have_the_number(self, step, number):
'''I have the number (\d+)'''
self.set_number(number)
def i_compute_its_factorial(self, step):
number = self.get_number()
self.set_number(factorial(number))
def check_number(self, step, expected):
'''I see the number (\d+)'''
self._assert_number_is(int(expected))
# Important!
# Steps are added only when you instanciate the "@steps" decorated class
# Internally decorator "@steps" build a closure with __init__
FactorialSteps(world)
def factorial(number):
number = int(number)
if (number == 0) or (number == 1):
return 1
else:
return number*factorial(number-1)
```

Have a nice lettuce...! ;)