Steve Piercy
2018-10-09 05915720f97df868e0b7dcff6e9b8eed964b8a90
commit | author | age
b731b5 1 .. _qtut_unit_testing:
PE 2
86e187 3 =============================
SP 4 05: Unit Tests and ``pytest``
5 =============================
b1b922 6
PE 7 Provide unit testing for our project's Python code.
86e187 8
b1b922 9
PE 10 Background
11 ==========
12
86e187 13 As the mantra says, "Untested code is broken code." The Python community has
SP 14 had a long culture of writing test scripts which ensure that your code works
15 correctly as you write it and maintain it in the future. Pyramid has always had
16 a deep commitment to testing, with 100% test coverage from the earliest
17 pre-releases.
b1b922 18
86e187 19 Python includes a :ref:`unit testing framework
SP 20 <python:unittest-minimal-example>` in its standard library. Over the years a
21 number of Python projects, such as :ref:`pytest <pytest:features>`, have
22 extended this framework with alternative test runners that provide more
23 convenience and functionality. The Pyramid developers use ``pytest``, which
24 we'll use in this tutorial.
b1b922 25
86e187 26 Don't worry, this tutorial won't be pedantic about "test-driven development"
SP 27 (TDD). We'll do just enough to ensure that, in each step, we haven't majorly
28 broken the code. As you're writing your code, you might find this more
29 convenient than changing to your browser constantly and clicking reload.
b1b922 30
86e187 31 We'll also leave discussion of `pytest-cov
9ce94f 32 <https://pytest-cov.readthedocs.io/en/latest/>`_ for another section.
86e187 33
b1b922 34
PE 35 Objectives
36 ==========
37
86e187 38 - Write unit tests that ensure the quality of our code.
b1b922 39
86e187 40 - Install a Python package (``pytest``) which helps in our testing.
SP 41
b1b922 42
PE 43 Steps
44 =====
45
34a7a8 46 #.  First we copy the results of the previous step.
b1b922 47
34a7a8 48     .. code-block:: bash
b1b922 49
34a7a8 50         cd ..; cp -r debugtoolbar unit_testing; cd unit_testing
b1b922 51
34a7a8 52 #.  Add ``pytest`` to our project's dependencies in ``setup.py`` as a :term:`Setuptools` "extra":
b1b922 53
34a7a8 54     .. literalinclude:: unit_testing/setup.py
SP 55         :language: python
56         :linenos:
beeb8e 57         :emphasize-lines: 15
b1b922 58
34a7a8 59 #.  Install our project and its newly added dependency.
b15a06 60     Note that we use the extra specifier ``[dev]`` to install testing requirements for development and surround it and the period with double quote marks.
b1b922 61
34a7a8 62     .. code-block:: bash
SP 63
23fbca 64         $VENV/bin/pip install -e ".[dev]"
34a7a8 65
SP 66 #.  Now we write a simple unit test in ``unit_testing/tutorial/tests.py``:
67
68     .. literalinclude:: unit_testing/tutorial/tests.py
69         :linenos:
70
71 #.  Now run the tests:
72
73     .. code-block:: bash
b1b922 74
PE 75
34a7a8 76         $VENV/bin/pytest tutorial/tests.py -q
SP 77         .
78         1 passed in 0.14 seconds
b1b922 79
PE 80
81 Analysis
82 ========
83
86e187 84 Our ``tests.py`` imports the Python standard unit testing framework. To make
SP 85 writing Pyramid-oriented tests more convenient, Pyramid supplies some
86 ``pyramid.testing`` helpers which we use in the test setup and teardown. Our
87 one test imports the view, makes a dummy request, and sees if the view returns
88 what we expect.
b1b922 89
86e187 90 The ``tests.TutorialViewTests.test_hello_world`` test is a small example of a
SP 91 unit test. First, we import the view inside each test. Why not import at the
92 top, like in normal Python code? Because imports can cause effects that break a
93 test. We'd like our tests to be in *units*, hence the name *unit* testing. Each
94 test should isolate itself to the correct degree.
b1b922 95
86e187 96 Our test then makes a fake incoming web request, then calls our Pyramid view.
SP 97 We test the HTTP status code on the response to make sure it matches our
98 expectations.
b1b922 99
PE 100 Note that our use of ``pyramid.testing.setUp()`` and
101 ``pyramid.testing.tearDown()`` aren't actually necessary here; they are only
102 necessary when your test needs to make use of the ``config`` object (it's a
103 Configurator) to add stuff to the configuration state before calling the view.
34a7a8 104
86e187 105
65687f 106 Extra credit
b1b922 107 ============
PE 108
86e187 109 #. Change the test to assert that the response status code should be ``404``
f63332 110    (meaning, not found). Run ``pytest`` again. Read the error report and see
86e187 111    if you can decipher what it is telling you.
b1b922 112
86e187 113 #. As a more realistic example, put the ``tests.py`` back as you found it, and
SP 114    put an error in your view, such as a reference to a non-existing variable.
115    Run the tests and see how this is more convenient than reloading your
116    browser and going back to your code.
b1b922 117
PE 118 #. Finally, for the most realistic test, read about Pyramid ``Response``
86e187 119    objects and see how to change the response code. Run the tests and see how
SP 120    testing confirms the "contract" that your code claims to support.
b1b922 121
PE 122 #. How could we add a unit test assertion to test the HTML value of the
123    response body?
124
125 #. Why do we import the ``hello_world`` view function *inside* the
126    ``test_hello_world`` method instead of at the top of the module?
127
34a7a8 128 .. seealso:: See also :ref:`testing_chapter` and `Setuptools Declaring "Extras" (optional features with their own dependencies) <https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies>`_.