Having stumbled upon freshen recently and not finding much about it on the web apart from the (entirely sufficient) documentation, I decided to use my seldom-updated blog to help remedy that situation.

Apparently, behavior driven development is the new buzzword (and it sounds good, too: define the new behaviors you want and then develop them), and cukes is a testing framework for ruby which everyone wants in their language. There is another bdd testing framework for python which required selenium and had a snazzy web page, but some broad gaps in their documentation when I was looking at it first. freshen, on the other hand, just plugs into nose, uses the cukes syntax and otherwise just works pretty well out of the box.

Here's how I used it: after plugging nose into your django tests, you can just run it with ''manage.py test --with-freshen''. Obviously, you want to have some tests defined first. Here are some of mine for babytimeline - now defunct:

Scenario: delete a baby
Given I am logged in
When I enter the url /baby/delete/2
And I enter the data {}
Then I see the template timeline/baby_confirm_delete.html but not the data id="baby_">

Scenario: create a milestone with free text
Given I am logged in
When I enter the url /baby/addmilestone/2/
And I enter the data {'date': '2013-04-01', 'baby': '2', 'note': 'hallolulila'}
Then I see the template timeline/milestone_form.html and the data hallolulila

In order to test this properly with the django test client, we need to load some data into the test database (unlike the normal django tests, that's not automatic), set up the client and empty the test mailbox (if you want to test email stuff, like for django-registration). Here's some of that code:

import re

from freshen import *
from django.test.client import Client
from django.core import management
from django.core import mail

@Before
def before(scenario):
    mail.outbox = []
    scc.client = Client()
    management.call_command('loaddata', 'test_data.json', verbosity=0)

@Given("I am logged in")
def login():
    scc.client.login(username="notauser", password="nottherealpasswordobviuosly")

@When("I enter the url (/.*)")
def press_button(button):
    scc.button = button
    scc.response_get = scc.client.get(button, follow=True)

@And("I enter the data (.*)")
def enter_data(data):
    dict = eval(data)
    scc.response_post = scc.client.post(scc.button, dict, follow=True)

@Then("I see the template ([^.]+.html) and the data (.*)")
def check_response_find(template_name, regex):
    assert scc.response_get.template[0].name == template_name
    assert scc.response_post.status_code == 200
    assert re.search(regex, scc.response_post.content)

@Then("I see the template ([^.]+.html) but not the data (.*)")
def check_response_not_find(template_name, regex):
    assert scc.response_get.template[0].name == template_name
    assert scc.response_post.status_code == 200
    assert not re.search(regex, scc.response_post.content)

In one evening, I managed to add acceptance tests for nearly all my user interactions on babytimeline (granted, it's a small application, with about 15 of these tests), and together with my unit tests (yeah, you still need those, even with bdd testing), I went from 74 % code coverage to 86% (and I think the nose code coverage plugin is a bit buggy anyway :). In short - I really like freshen, it makes bdd in python easy and elegant (apart from the clunky global variables...)!