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

Test Everything

Dave Dash
September 11, 2011

Test Everything

Testing in Django is easy if you're testing models against your database. What happens when you need to test other-systems, like a search engine, or an API? This tutorial will cover how I built SphinxTestCase, ESTestCase and redisutils to allow us to maintain test coverage on our web sites.

Dave Dash

September 11, 2011
Tweet

More Decks by Dave Dash

Other Decks in Programming

Transcript

  1. me (the djangonaut) sr web developer @ mozilla helped move

    mozilla webdev to Django worked on Firefox Add-ons Technical lead for Firefox Input I work on internal libraries, tools, and some smaller sites. i blog @ davedash.com i email @ [email protected] i tweet @davedash 2
  2. mozilla webdev pronounced mo-zilla 40+ people 100+ web sites we

    happily churn out PEP-8 compliant code Django shop since early 2010 we create and support most web sites *.mozilla.com / *.mozilla.org Mozilla Developer Network Firefox Add-ons Firefox Support Mozilla.org and more... actively hiring, if this sort of thing appeals to you 3
  3. What we’ll cover philosophy of testing ./manage.py test Writing TestCases

    Code coverage Testing difficult things in the django test suite • • • • • 4
  4. Follow along Etherpad - http://mzl.la/django-test +1 any interesting topics List

    any testing issues or general questions you might have • • • 6
  5. Introduce yourself Name Involvement with Django Testing experience What you

    want to get out of this morning • • • • 7
  6. Agenda I’ll start a fight Testing @ Mozilla Interactive stuff

    How Django’s testing works How we can make testing better Testing beyond the DB Gotchas 8
  7. please... Ask questions. This is 3 hours... I can’t fill

    it all with slides. Correct me if I’m wrong. • • 9
  8. “Instead of writing tests I try to be extremely careful

    in coding, and keep the code size small so I continue to understand it.” -maciej ceglowski, pinboard.in 15
  9. 64

  10. 70

  11. 75

  12. 85

  13. 87

  14. 90

  15. 91

  16. 92

  17. 98 class TestCase1(TestCase): fixtures = ['a'] class TestCase2(TestCase): fixtures =

    ['a', 'b'] class TestCase3(TestCase): fixtures = ['a'] class TestCase4(TestCase): fixtures = ['a', 'c'] class TestCase5(TestCase): fixtures = ['a'] class TestCase6(TestCase): fixtures = ['a', 'c']
  18. 105

  19. 108

  20. 111

  21. 113 ................................S....................................................F................................................................ ====================================================================== FAIL: Verify the "next" pagination link appears

    and directs the user to the ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/dash/Projects/input/reporter/apps/search/tests/test_dashboard.py", line 57, in test_beta_pagination_link eq_(len(pag_link), 1) File "/Users/dash/Projects/input/reporter/vendor/packages/nose/nose/tools.py", line 31, in eq_ assert a == b, msg or "%r != %r" % (a, b) AssertionError: 0 != 1 ---------------------------------------------------------------------- Ran 150 tests in 32.504s FAILED (SKIP=1, failures=1)
  22. 115 apps/input/tests/test_middleware.py:MiddlewareTests ........... apps/input/tests/test_redirects.py:RedirectTests ... apps/myadmin/tests.py:ViewTestCase ...... apps/search/tests/test_client.py:SearchTest .............. apps/search/tests/test_client.py

    .. ====================================================================== FAIL: apps/search/tests/test_dashboard.py:TestDashboard.test_beta_pagination_link ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/dash/Projects/input/reporter/apps/search/tests/test_dashboard.py", line 57, in test_beta_pagination_link eq_(len(pag_link), 1) File "/Users/dash/Projects/input/reporter/vendor/packages/nose/nose/tools.py", line 31, in eq_ assert a == b, msg or "%r != %r" % (a, b) AssertionError: 0 != 1 apps/search/tests/test_dashboard.py:TestDashboard .. apps/search/tests/test_dashboard.py:TestHelpers ........ apps/search/tests/test_dashboard.py:TestMobileDashboard . apps/search/tests/test_elastic.py:TestElastic ..
  23. 117

  24. 122 Name Stmts Miss Cover Missing ---------------------------------------------------------------- apps/search/__init__ 0 0

    100% apps/search/client 193 4 98% 96-97, 134-137 apps/search/context_processors 5 0 100% apps/search/cron 13 13 0% 1-24 apps/search/forms 70 2 97% 131, 136 apps/search/helpers 136 1 99% 154 apps/search/models 0 0 100% apps/search/tasks 8 0 100% apps/search/tests/__init__ 26 1 96% 27 apps/search/tests/test_client 75 0 100% apps/search/tests/test_dashboard 105 1 99% 58 apps/search/tests/test_elastic 15 0 100% apps/search/tests/test_views 193 0 100% apps/search/urls 3 0 100% apps/search/utils 10 0 100% apps/search/views 142 1 99% 166 ---------------------------------------------------------------- TOTAL 994 23 98%
  25. 147 class RankingTest(SphinxTestCase): """This test assures that we don't regress

    our rankings.""" fixtures = ('base/users', 'base/addon_1833_yoono', 'base/addon_9825_fastestfox', 'base/addon_5579', 'base/addon_personas-plus', ) def test_twitter(self): """ Search for twitter should yield Yoono before FastestFox since Yoono has "twitter" in it's name field. """ r = query('twitter') eq_(r[0].id, 1833) eq_(r[1].id, 9825) def test_cool(self): """Search for cool should return CoolIris before PersonasPlus.""" r = query('cool') eq_(r[0].slug, 'cooliris') eq_(r[1].slug, 'personas-plus')
  26. 152 if not connections: # don't set this repeatedly for

    alias, backend in settings.REDIS_BACKENDS.items(): _, server, params = parse_backend_uri(backend) try: socket_timeout = float(params.pop('socket_timeout')) except (KeyError, ValueError): socket_timeout = None password = params.pop('password', None) if ':' in server: host, port = server.split(':') try: port = int(port) except (ValueError, TypeError): port = 6379 else: host = 'localhost' port = 6379 connections[alias] = redislib.Redis(host=host, port=port, db=0, password=password, socket_timeout=socket_timeout) def mock_redis(): ret = dict(connections) for key in connections: connections[key] = MockRedis() return ret
  27. 167 class SphinxTestCase(TestCase): """ This test case type can setUp

    and tearDown the sphinx daemon. Use this when testing any feature that requires sphinx. """ fixtures = ['users.json', 'search/documents.json', 'posts.json', 'questions.json'] @classmethod def setup_class(cls): super(SphinxTestCase, cls).setup_class() if not settings.SPHINX_SEARCHD or not settings.SPHINX_INDEXER: raise SkipTest() os.environ['DJANGO_ENVIRONMENT'] = 'test' if os.path.exists(settings.TEST_SPHINX_PATH): shutil.rmtree(settings.TEST_SPHINX_PATH) os.makedirs(os.path.join(settings.TEST_SPHINX_PATH, 'data')) os.makedirs(os.path.join(settings.TEST_SPHINX_PATH, 'log')) os.makedirs(os.path.join(settings.TEST_SPHINX_PATH, 'etc')) reindex() start_sphinx() time.sleep(1) @classmethod def teardown_class(cls): stop_sphinx() super(SphinxTestCase, cls).teardown_class()
  28. I’m looking for new coworkers Mozilla is a fun place

    to work It’s more exciting than these slides We are a developer friendly python shop We build things and share them • • • • 184
  29. Perks desks computers chairs (if you want) work remotely fantastic

    coworkers <= seriously • • • • • 185