89
Django Testing Eric Holscher http://ericholscher.com Django Testing 1 Tuesday, May 5, 2009

Django Testing

Embed Size (px)

DESCRIPTION

Talk about where Testing is in Django, and how to get started doing it.

Citation preview

Page 1: Django Testing

Django TestingEric Holscher

http://ericholscher.com

Django Testing

1Tuesday, May 5, 2009

Page 2: Django Testing

How do you know?

• First 4 months of my job was porting and testing Ellington

• Going from Django r1290 to Django 1.0.

• Suite from 0 to 400 tests.

2Tuesday, May 5, 2009

Page 3: Django Testing

30,000 Ft View

• State of testing in Django

• Why you should be testing

• How you start testing

• Useful tools

• Eventual Goals

3Tuesday, May 5, 2009

Page 4: Django Testing

State of Django Testing

4Tuesday, May 5, 2009

Page 5: Django Testing

assertTrue('Hello World', community.testing.status)

5Tuesday, May 5, 2009

Page 6: Django Testing

Django 1.1

Making Testing Possible since 2009

6Tuesday, May 5, 2009

Page 7: Django Testing

manage.py startapp creates a tests.py

7Tuesday, May 5, 2009

Page 8: Django Testing

from django.test import TestCase

class SimpleTest(TestCase): def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. """ self.failUnlessEqual(1 + 1, 2)

__test__ = {"doctest": """Another way to test that 1 + 1 is equal to 2.

>>> 1 + 1 == 2True"""}

8Tuesday, May 5, 2009

Page 9: Django Testing

Fast Tests(Transactions)

9Tuesday, May 5, 2009

Page 10: Django Testing

0

15

30

45

60

Django 1.0 Django 1.1

Ellington Test Speedup

Minutes (Lower is better)

10Tuesday, May 5, 2009

Page 11: Django Testing

You now have no excuse.

11Tuesday, May 5, 2009

Page 12: Django Testing

Why to test

12Tuesday, May 5, 2009

Page 13: Django Testing

Scary13Tuesday, May 5, 2009

Page 14: Django Testing

Less Scary14Tuesday, May 5, 2009

Page 15: Django Testing

Not Scary15Tuesday, May 5, 2009

Page 16: Django Testing

Peace of Mind16Tuesday, May 5, 2009

Page 17: Django Testing

Code must adapt

17Tuesday, May 5, 2009

Page 18: Django Testing

“It is not the strongest of the species that survives, nor the most intelligent, but the

one most responsive to change.”

- Charles Darwin

18Tuesday, May 5, 2009

Page 19: Django Testing

Won’t somebody please think of the users?!

19Tuesday, May 5, 2009

Page 20: Django Testing

Tests as Documentation

20Tuesday, May 5, 2009

Page 21: Django Testing

Tests as Documentation

Test driven development +

Document driven development=

Test Driven Documentation

20Tuesday, May 5, 2009

Page 22: Django Testing

“Code without tests is broken as designed”- Jacob Kaplan-Moss

21Tuesday, May 5, 2009

Page 23: Django Testing

Dizzying Array of Testing Options

22Tuesday, May 5, 2009

Page 24: Django Testing

What kind of test?

• doctest

• unittest

23Tuesday, May 5, 2009

Page 25: Django Testing

Doctests

• Inline documentation

• <Copy from terminal to test file>

• Can’t use PDB

• Hide real failures

• “Easy”

24Tuesday, May 5, 2009

Page 26: Django Testing

def parse_ttag(token, required_tags): """ A function to parse a template tag.

It sets the name of the tag to 'tag_name' in the hash returned.

>>> from test_utils.templatetags.utils import parse_ttag >>> parse_ttag('super_cool_tag for my_object as obj', ['as']) {'tag_name': u'super_cool_tag', u'as': u'obj'} >>> parse_ttag('super_cool_tag for my_object as obj', ['as', 'for']) {'tag_name': u'super_cool_tag', u'as': u'obj', u'for': u'my_object'}

""" bits = token.split(' ') tags = {'tag_name': bits.pop(0)} for index, bit in enumerate(bits): bit = bit.strip() if bit in required_tags: if len(bits) != index-1: tags[bit] = bits[index+1] return tags

25Tuesday, May 5, 2009

Page 27: Django Testing

Unit Tests

• Use for everything else

• More rubust

• setUp and tearDown

• Standard (XUnit)

26Tuesday, May 5, 2009

Page 28: Django Testing

import randomimport unittest

class TestRandom(unittest.TestCase):

def setUp(self): self.seq = range(10)

def testshuffle(self): # make sure the shuffled sequence does not lose any elements random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))

if __name__ == '__main__': unittest.main()

27Tuesday, May 5, 2009

Page 29: Django Testing

Django TestCase

• Subclasses unittest

• Fixtures

• Assertions

• Mail

• URLs

28Tuesday, May 5, 2009

Page 30: Django Testing

Test Client

• Test HTTP Requests without server

• Test Views, Templates, and Context

29Tuesday, May 5, 2009

Page 31: Django Testing

from django.contrib.auth.models import Userfrom django.test import TestCasefrom django.core import mail

class PasswordResetTest(TestCase): fixtures = ['authtestdata.json'] urls = 'django.contrib.auth.urls'

def test_email_not_found(self): "Error is raised if the provided email address isn't currently registered" response = self.client.get('/password_reset/') self.assertEquals(response.status_code, 200) response = self.client.post('/password_reset/', {'email': '[email protected]'}) self.assertContains(response, "That e-mail address doesn&#39;t have an associated user account") self.assertEquals(len(mail.outbox), 0)

30Tuesday, May 5, 2009

Page 32: Django Testing

What flavor of test?

• Unit

• Functional

• Browser

31Tuesday, May 5, 2009

Page 33: Django Testing

Unit test

• Low level tests

• Small, focused, exercising one bit of functionality

32Tuesday, May 5, 2009

Page 34: Django Testing

Regression test

• Written when you find a bug

• Proves bug was fixed

• Django Tickets

33Tuesday, May 5, 2009

Page 35: Django Testing

Functional

• “Black Box Testing”

• Check High Level Functionality

34Tuesday, May 5, 2009

Page 36: Django Testing

Functional Testing Tools

• Twill

• Django Test Client

• ...

35Tuesday, May 5, 2009

Page 37: Django Testing

Ellington

• Lots of different clients

• Need to test deployment (Functional)

36Tuesday, May 5, 2009

Page 38: Django Testing

go {{ site.url }}/marketplace/search/formvalue 1 q pizzasubmitcode 200

37Tuesday, May 5, 2009

Page 39: Django Testing

38Tuesday, May 5, 2009

Page 40: Django Testing

Pretty Functional Tests39Tuesday, May 5, 2009

Page 41: Django Testing

Can test

• All sites on a server

• All sites of a certain type

• A single problemed client

• A test across all sites

40Tuesday, May 5, 2009

Page 42: Django Testing

Browser tests

• Run tests in a web browser

• Check compatibility of design

• Basically an IE sanity check

• Only real way to test JS, AJAX, CSS

41Tuesday, May 5, 2009

Page 43: Django Testing

Browser Testing Tools

• Windmill

• Selenium

42Tuesday, May 5, 2009

Page 44: Django Testing

43Tuesday, May 5, 2009

Page 45: Django Testing

Other kinds of testing

• Spiders

• Fuzz testing

• Load testing

• Prayer

44Tuesday, May 5, 2009

Page 46: Django Testing

Where do I start?

45Tuesday, May 5, 2009

Page 47: Django Testing

Fixed a bug

46Tuesday, May 5, 2009

Page 48: Django Testing

Poking at code on the command line

47Tuesday, May 5, 2009

Page 49: Django Testing

Pony turned Horse

48Tuesday, May 5, 2009

Page 50: Django Testing

Now what?

49Tuesday, May 5, 2009

Page 51: Django Testing

Start with a regression test or a functional test

50Tuesday, May 5, 2009

Page 52: Django Testing

Use unittest unless you have a reason not to!

51Tuesday, May 5, 2009

Page 53: Django Testing

Use the data, Luke

• Use data as a pivot

• Fixtures means you use Unit Tests

• Creation on the command line, Doctests

52Tuesday, May 5, 2009

Page 54: Django Testing

Creating Fixtures

• ./manage.py dumpdata <app>

• ./manage.py makefixture Model[x:y]

• Follows relations

• By Hand

53Tuesday, May 5, 2009

Page 55: Django Testing

TestShell

./manage.py testshell <fixture>

54Tuesday, May 5, 2009

Page 56: Django Testing

Making Functional tests

• Usually a relatively annoying process

• Testmaker makes it easy.

• ./manage.py testmaker <app>

• Simply browse and your session is recorded.

55Tuesday, May 5, 2009

Page 57: Django Testing

Testing your views generally gets you the

most coverage.

56Tuesday, May 5, 2009

Page 58: Django Testing

80% Case

57Tuesday, May 5, 2009

Page 59: Django Testing

When > Where

58Tuesday, May 5, 2009

Page 60: Django Testing

Tools

59Tuesday, May 5, 2009

Page 61: Django Testing

Coverage

60Tuesday, May 5, 2009

Page 62: Django Testing

61Tuesday, May 5, 2009

Page 63: Django Testing

Mock Objects

• http://www.voidspace.org.uk/python/mock/

62Tuesday, May 5, 2009

Page 64: Django Testing

import unittestfrom mock import Mock from templatetags.cms_tags import if_link_is_active, IsActiveNode class TestIsActiveTag(unittest.TestCase): def test_returns_correct_node_type(self): token = Mock(methods=['split_contents']) token.split_contents.return_value = ('if_link_is_active', 'bar') self.assertEqual(type(if_link_is_active(Mock(), token)), IsActiveNode)

63Tuesday, May 5, 2009

Page 65: Django Testing

Custom Test Runners

64Tuesday, May 5, 2009

Page 66: Django Testing

Django Test Extensions

• Gareth Rushgrove

• Extra Assertions

• Coverage and XML Test Runners

• http://github.com/garethr/django-test-extensions

65Tuesday, May 5, 2009

Page 67: Django Testing

Django Sane Testing

• Ella Folk, Lucas (Hi!)

• Based on nosetests

• Selenium

• Live server

• http://devel.almad.net/trac/django-sane-testing/

66Tuesday, May 5, 2009

Page 68: Django Testing

Django Test Utils

• Mine!

• Testmaker

• Crawler

• Random fanciness

• http://github.com/ericholscher/django-test-utils/tree/master

67Tuesday, May 5, 2009

Page 69: Django Testing

Recording tests is generally seen as bad.

68Tuesday, May 5, 2009

Page 70: Django Testing

My philosophy

• Write tests.

• Notice patterns and best practices

• Automate recording of tests with those patterns

• If you can’t automate, use tools to make it easier.

69Tuesday, May 5, 2009

Page 71: Django Testing

Process for testmaker

• Most view tests check status_code and response context

• Write middleware that catches this info

• Records it to a file

• Err on the side of more data.

70Tuesday, May 5, 2009

Page 72: Django Testing

Goals(That perfect world)

71Tuesday, May 5, 2009

Page 73: Django Testing

Some form of TDD

• Write tests as you write code

• Makes your code easy to test

72Tuesday, May 5, 2009

Page 74: Django Testing

Follow Django’s Model

• Tests with every commit

• Docs with every commit

• Run tests before commiting

73Tuesday, May 5, 2009

Page 75: Django Testing

Use a DVCS

• At work we have a central SVN repo

• Git feature branches

• Code is staged for documentation and testing

• Committed to SVN once it is “done”

74Tuesday, May 5, 2009

Page 76: Django Testing

Continuous Integration

75Tuesday, May 5, 2009

Page 77: Django Testing

NEVER LEAVE THE BUILD BROKEN

76Tuesday, May 5, 2009

Page 78: Django Testing

Love Green77Tuesday, May 5, 2009

Page 79: Django Testing

Fast(er) Tests

78Tuesday, May 5, 2009

Page 80: Django Testing

JSON

79Tuesday, May 5, 2009

Page 81: Django Testing

Profiling

python -m cProfile manage.py test

80Tuesday, May 5, 2009

Page 82: Django Testing

Mock Objects

http://www.voidspace.org.uk/python/mock/

81Tuesday, May 5, 2009

Page 83: Django Testing

Future and Ponies

82Tuesday, May 5, 2009

Page 84: Django Testing

Summer of Code

• Test-Only Models

• Coverage

• Windmill tests of the admin

83Tuesday, May 5, 2009

Page 85: Django Testing

Test Suites in Django

84Tuesday, May 5, 2009

Page 86: Django Testing

Central Testing Repository

85Tuesday, May 5, 2009

Page 87: Django Testing

Nose Plugin Integration

86Tuesday, May 5, 2009

Page 88: Django Testing

Things to remember

• Testing is not hard, you just have to get started.

• If your code doesn’t have tests, it will be hard/impossible to refactor

• Once you have tests, you need to run them!

87Tuesday, May 5, 2009