5: Unit and Functional Tests

Test coverage is a badge of honor for Pyramid, which has 100% test (and documentation) coverage from its inception. Equally, Pyramid wants to help Pyramid developers be productive in writing tests for their code, providing facilities to promote this.

Pyramid has a first-class concept of configuration distinct from code. This approach is optional, but its presence makes it distinct from other Python web frameworks.

Sure, testing helps ensure future quality and facilitate refactoring. But it also makes up-front development faster, particularly in smart editors and IDEs. Restarting your app and clicky-clicking in your browser is a drag.

Objectives

  • Provide both unit and functional tests for our Wiki application
  • Create a new module tutorial/tests.py
  • Write unit tests and write functional tests using WebTest
  • Run both under the nose test runner

Steps

  1. Again, let’s use the previous package as a starting point for a new distribution, then making it active. We will also install nose and WebTest:

    (env33)$ cd ../..; cp -r step03 step05; cd step05
    (env33)$ python3.3 setup.py develop
    (env33)$ easy_install-3.3 nose webtest
    
  2. Create a module for our unit and functional tests in tutorial/tests.py:

     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
    import unittest
    
    from pyramid import testing
    
    
    class ViewTests(unittest.TestCase):
        def setUp(self):
            self.config = testing.setUp()
    
        def tearDown(self):
            testing.tearDown()
    
        def test_my_view(self):
            from tutorial.views import hello_world
            request = testing.DummyRequest()
            response = hello_world(request)
            self.assertEqual(response.status, '200 OK')
    
    
    class FunctionalTests(unittest.TestCase):
        def setUp(self):
            from tutorial import main
            settings = {}
            app = main(settings)
            from webtest import TestApp
            self.testapp = TestApp(app)
    
        def test_it(self):
            res = self.testapp.get('/', status=200)
            self.assertIn(b'Hello', res.body)
    
  3. Now run the tests in your package using nose:

    (env33)$ nosetests .
    ..
    -----------------------------------------------------------------
    Ran 2 tests in 1.971s
    
    OK
    

Analysis

Our unit tests are first. We have one unit test, which simply executes our view and ensures it returns the proper status code.

Our functional test is second. To run, WebTest needs to be pointed at the callable that returns our WSGI application. We then execute a web request at the / URL and ensure that it returns a body with a string we are expecting.

Extra Credit

  1. How does nose know the tests are in tests.py?
  2. Did WebTest really launch an HTTP server and issue an HTTP request?
  3. If your code generates an error, will Pyramid handle it gracefully?

Discussion

  • Pyramid and the commitment to test coverage
  • Philosophies on unit tests vs. integration tests vs. functional tests vs. doctests
  • The challenge in setup/teardown regarding configuration, registries, and machinery under the surface (both the frameworks and yours!)

Table Of Contents