Login

Django and Twill

Author:
spookylukey
Posted:
March 25, 2008
Language:
Python
Version:
.96
Score:
7 (after 7 ratings)

Django's test client is very limited when it comes to testing complex interactions e.g. forms with hidden or persisted values etc. Twill excels in this area, and thankfully it is very easy to integrate it.

  • Use twill_setup in your TestCaseSubClass.setUp() method
  • Use twill_teardown in TestCaseSubClass.tearDown() method
  • In a test, use something like make_twill_url() to generate URLs that will work for twill.
  • Use twill.commands.go() etc. to control twill, or use twill.execute_string() or twill.execute_script().
  • Add twill.set_output(StringIO()) to suppress twill output
  • If you want to write half the test, then use twill interactively to write the rest as a twill script, use the example in unfinished_test()

Twill will raise exceptions if commands fail. This means you will get 'E' for error, rather than 'F' for fail in the test output. If necessary, it wouldn't be hard to wrap the twill commands to flag failure with TestCase.assert_

There are, of course, more advanced ways of using these functions (e.g. a mixin that does the setup/teardown for you), but the basic functions needed are here.

See also:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# Test module

import twill
from twill import commands as tc
from twill.shell import TwillCommandLoop
from django.test import TestCase
from django.core.servers.basehttp import AdminMediaHandler
from django.core.handlers.wsgi import WSGIHandler
from StringIO import StringIO

# Functions you'll need:

def twill_setup():
    app = AdminMediaHandler(WSGIHandler())
    twill.add_wsgi_intercept("127.0.0.1", 8080, lambda: app)

def twill_teardown():
    twill.remove_wsgi_intercept('127.0.0.1', 8080)

def make_twill_url(url):
    # modify this
    return url.replace("http://www.yourwebsite.com/", 
                       "http://127.0.0.1:8080/")

def twill_quiet():
    # suppress normal output of twill.. You don't want to 
    # call this if you want an interactive session
    twill.set_output(StringIO())

# Example tests. (This code has not been tested, as it 
# would require a website to exist, but very similar 
# code is working on my machine)

class MyTest(TestCase):
    def setUp(self):
        twill_setup()

    def tearDown(self):
        twill_teardown()

    def test1(self):
        url = "http://www.yourwebsite.com/signup/"
        twill_quiet()
        tc.go(make_twill_url(url))
        tc.find("Sign up") # sanity check
        # Fill in the form
        tc.fv('1', 'user_name', 'newuser1')
        tc.fv('1', 'email', '[email protected]')
        tc.submit()
        tc.find("A confirmation email has been sent")

    def unfinished_test(self):
        # Some complicated set up here, perhaps
        url = "http://www.yourwebsite.com/prefs/"
        tc.go(make_twill_url(url))

        # The following will launch into an interactive
        # twill session
        cmd = TwillCommandLoop()
        cmd.cmdloop()
  
# -----------------------------------------------------------------
# In your settings.py used for testing, add this:
DEBUG_PROPAGATE_EXCEPTIONS = True
# This will allow exception generated in view code or templates to propagate,
# otherwise they are caught by Django in an unhelpful way.  Available from
# trunk rev 7537

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 9 months ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 9 months, 1 week ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 3 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 4 months ago
  5. Help text hyperlinks by sa2812 1 year, 5 months ago

Comments

osborn.steven (on March 10, 2009):

Thank you, this has been very helpful.

#

Avang2000 (on April 6, 2010):

Thanks for useful code. But how to make it works with cookies?

#

Please login first before commenting.