More than a side salad: behaviour driven testing and test driven design in Django with Lettuce

Preview:

DESCRIPTION

 

Citation preview

more than aside salad:behaviour driven testing andtest driven design in Djangowith Lettuce

Danielle Madeley blogs.gnome.org/danni dannipenguin

Just what is testdrivendevelopment?

Tests first;code second

http://www.nilkanth.com/2007/06/08/three­monkeys­of­test­driven­development/

But how do wewrite tests whenwe don't knowwhat the codelooks like?

Don't test code;test behaviours

As a visitor to the siteI want to create an account using my GoogleloginSo that I can log in without needing anotherpassword

GivenI have a valid Google account

WhenI visit the siteAnd I click create account

ThenI am redirected to Google

http://www.flickr.com/photos/27369469@N08/2660160225

Lettucehttp://lettuce.it

http://www.flickr.com/photos/65567316@N00/2742564457

Feature: Authenticate to API

As a searcher/API user I want to authenticate to the API So that I can make queries

Scenario: I am not authenticated to the API When I get the resource "/api/v3/hello/"

Then I get the response code 401

Steps

http://www.flickr.com/photos/60364452@N00/504844510

from lettuce import step, world

@step(r'I get the resource "([̂"]*)"')def get_resource(step, url): """ Make a GET request to the given URL """

world.response = world.client.get(url)

Steps arestateful

http://www.flickr.com/photos/chrisconnell/3201514924/

from django.test.client import Clientfrom lettuce import before, step, worldfrom nose.tools import assert_equals

@before.each_scenariodef set_default_client(scenario):

world.client = Client()

@step(r'I get the resource "([̂"]*)"')def get_resource(step, url):

world.response = world.client.get(url)

@step(r'I get the response code (\d+)')def check_response_code(step, code):

code = int(code) assert_equals(world.response.status_code, code)

./manage.pyharvest

# settings.py

INSTALLED_APPS = ( ... 'lettuce.django', 'myapp',)

LETTUCE_APPS = ( 'myapp',)

LETTUCE_USE_TEST_DATABASE = True

Selenium

http://www.flickr.com/photos/21663307@N02/3599012763

from lettuce import before, world# import external lettuce stepsimport lettuce_webdriver.webdriver

@before.alldef set_browser(): """ Create a browser instance for use in tests. """

world.browser = webdriver.PhantomJS(...) world.browser.set_window_size(1200, 800)

def site_url(url):

base_url = 'http://%s' % socket.gethostname()

if server.port is not 80: base_url += ':%d' % server.port

return urlparse.urljoin(base_url, url)

@step("I visit the site")def open_site(step):

step.given('I visit "%s"' % site_url('/'))

Scenario: Add consumer with blank Contact name When I log in to admin with username "admin" and password "secret" And I click "Consumers" And I click "Add consumer" And I fill in "Organisation" with "OOOO" And I fill in "Contact name" with "" And I fill in "Contact number" with "0399999999" And I fill in "Website" with "www.test.com" And I press "Save"

Then I should see "This field is required" And there should be 0 consumers in the database

Fixtures

Feature: Create new API key

As an administrator I want to create a new API key So that I can enable others to query API

Background: Given I have users in the database: | username | password | is_superuser | | admin | secret | true |

Built in steps usingDjango's modelintrospectionGiven I have users in the database: ...

Then there should be 1 consumer in the database

And consumer should be present in the database: ...

Which are extendable@creates_models(User)def create_user(step): data = hashes_data(step) for hash_ in data: is_superuser = hash_.pop('is_superuser', False

if is_superuser: user = User.objects.create_superuser(**hash_) else: user = User.objects.create_user(**hash_)

user.save()

reset_sequence(User)

Best practice

Given I have items in my cart

When I go to checkoutAnd I pay for the items

Then everything worked

write reusablesteps

write reusable scenariosScenario Outline: I edit a user to add a team Given users have permissions: | user | organisation | permission | | peon@org.org | OrgCorp | organisation_user | And I log in with email "<email>" through the profile server

When I visit site page "organisation/1/admin/user/4/edit" And I select option "Team 1" from selector "Team" And I press "Save"

Then there should be 1 organisation member metadata in the database Then organisation member metadata should be present in the database: | organisation__name | user__email | team | | OrgCorp | peon@org.org | Team 1 |

Examples: | email | | god@god.org | | superuser@org.org | | poweruser@org.org |

tie each feature fileto a single story

include scenariosand anti-scenarios

Other uses

http://www.flickr.com/photos/omcoc/3050378171/

fin ;-Pquestions?

colophon This presentation was done in reveal.js using Junction, Cantarelland Source Code Pro with photography from the Creative Commons.

Infoxchange is hiring. Come and chat to me!

blogs.gnome.org/danni dannipenguin

http://www.flickr.com/photos/mau3ry/3763640652/

Recommended