Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Secrets of the Testing Masters

Secrets of the Testing Masters

Django ship with a wide range of tools to help you test your web application, but some of the best tools for testing Django don't come in the box.

In this talk, you'll get a brief introduction to two of those tools - Mock and Factory Boy - showing when they should be used, and some practical examples of their usage in a Django test suite.

Russell Keith-Magee

July 05, 2013
Tweet

More Decks by Russell Keith-Magee

Other Decks in Technology

Transcript

  1. class MyTest(TestCase): fixtures = [‘test_user’] def test_stuff(self): user = User.objects.get(pk=1)

    staff = User.objects.get(pk=100) self.assertFalse(user.is_staff) self.assertTrue(staff.is_staff)
  2. [ { "pk": 100, "model": "auth.user", "fields": { "username": "super",

    "first_name": "Super", "last_name": "User", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2007-05-30 13:20:10", "groups": [], "user_permissions": [], "password": "sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158", "email": "[email protected]", "date_joined": "2007-05-30 13:20:10" } } ]
  3. Fixtures: maybe not • Easy to create • Hard to

    read • Hard to update • No documentation
  4. class MyTest(TestCase): def test_stuff(self): user = UserF() staff = UserF(is_staff=True)

    self.assertFalse(user.is_staff) self.assertTrue(staff.is_staff)
  5. Sequence attributes class UserF(factory.Factory): FACTORY_FOR = models.User first_name = 'John'

    last_name = 'Doe' is_staff = False email = factory.Sequence( lambda n: 'user%[email protected]' % n )
  6. >>> user0 = UserF() >>> user0.first_name 'John' >>> user0.email '[email protected]'

    >>> user1 = UserF() >>> user1.first_name 'John' >>> user1.email '[email protected]'
  7. Lazy attributes class UserF(factory.Factory): FACTORY_FOR = models.User first_name = 'John'

    last_name = 'Doe' is_staff = False email = factory.LazyAttribute( lambda a: '{0}.{1}@example.com'.format( a.first_name, a.last_name ).lower() )
  8. >>> user0 = UserF() >>> user0.first_name 'John' >>> user0.email '[email protected]'

    >>> user1 = UserF(first_name=‘Frank’) >>> user1.first_name 'Frank' >>> user1.email '[email protected]'
  9. SubFactories class BlogEntryF(factory.Factory): FACTORY_FOR = models.BlogEntry title = 'My Blog

    Entry' body = 'This is the content.' author = factory.SubFactory(UserF)
  10. >>> entry0 = BlogEntryF() >>> entry0.title 'My Blog Entry' >>>

    entry0.author.email '[email protected]' >>> entry0.author.pk 1 >>> entry1 = BlogEntryF() >>> entry1.title 'My Blog Entry' >>> entry1.author.email '[email protected]' >>> entry1.author.pk 2
  11. >>> user1 = UserF(first_name=‘Frank’) >>> user1.first_name 'Frank' >>> user1.email '[email protected]'

    >>> entry0 = BlogEntryF(author=user1) >>> entry0.title 'My Blog Entry' >>> entry0.author.email '[email protected]'
  12. Other nifty features • F.build() • create, but don’t save

    • F.attributes() • dict of default attribute values • def create(self, **kwargs): • Override creation process
  13. Getting factory_boy • pip install factory_boy • https://github.com/rbarrois/factory_boy • pip

    install django-factory_boy • https://github.com/rbarrois/django-factory_boy
  14. mock: organized monkey patching • Methods to help your replace

    calls with reliable alternatives • Allows you to “program” expected results • Allows you to assert API usage
  15. Mock() • Dummy object that tracks: • how it is

    accessed • how methods are invoked • how often methods are invoked • Has no side effects • ...unless you program them
  16. >>> from mock import Mock >>> obj = SomeObject() >>>

    obj.method = Mock(return_value=3) >>> obj.method(3, 4, 5, key='value') 3 >>> obj.method.assert_called_with( 3, 4, 5, key='value')
  17. @patch • Declare an object in a namespace • Replace

    it with a Mock() • Restore original on exit
  18. Example: requests class LoginTests(TestCase): @patch('requests.get') def test_bad_login(self, r_get): r_get.return_value =

    Mock( status_code=401, text="Bad Login" ) with self.assertRaises(LoginFail): MyService.login() r_get.assert_called_with(‘/login/’)
  19. Getting mock • Builtin in Python 3.3 (unittest.mock) • pip

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