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 |
6312eb
|
32 |
<http://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 |
|
86e187
|
46 |
#. First we copy the results of the previous step, as well as install the |
SP |
47 |
``pytest`` package: |
b1b922
|
48 |
|
PE |
49 |
.. code-block:: bash |
|
50 |
|
187104
|
51 |
$ cd ..; cp -r debugtoolbar unit_testing; cd unit_testing |
d68cbc
|
52 |
$ $VENV/bin/pip install -e . |
86e187
|
53 |
$ $VENV/bin/pip install pytest |
b1b922
|
54 |
|
PE |
55 |
#. Now we write a simple unit test in ``unit_testing/tutorial/tests.py``: |
|
56 |
|
|
57 |
.. literalinclude:: unit_testing/tutorial/tests.py |
|
58 |
:linenos: |
|
59 |
|
|
60 |
#. Now run the tests: |
|
61 |
|
|
62 |
.. code-block:: bash |
|
63 |
|
|
64 |
|
86e187
|
65 |
$ $VENV/bin/py.test tutorial/tests.py -q |
b1b922
|
66 |
. |
86e187
|
67 |
1 passed in 0.14 seconds |
b1b922
|
68 |
|
PE |
69 |
|
|
70 |
Analysis |
|
71 |
======== |
|
72 |
|
86e187
|
73 |
Our ``tests.py`` imports the Python standard unit testing framework. To make |
SP |
74 |
writing Pyramid-oriented tests more convenient, Pyramid supplies some |
|
75 |
``pyramid.testing`` helpers which we use in the test setup and teardown. Our |
|
76 |
one test imports the view, makes a dummy request, and sees if the view returns |
|
77 |
what we expect. |
b1b922
|
78 |
|
86e187
|
79 |
The ``tests.TutorialViewTests.test_hello_world`` test is a small example of a |
SP |
80 |
unit test. First, we import the view inside each test. Why not import at the |
|
81 |
top, like in normal Python code? Because imports can cause effects that break a |
|
82 |
test. We'd like our tests to be in *units*, hence the name *unit* testing. Each |
|
83 |
test should isolate itself to the correct degree. |
b1b922
|
84 |
|
86e187
|
85 |
Our test then makes a fake incoming web request, then calls our Pyramid view. |
SP |
86 |
We test the HTTP status code on the response to make sure it matches our |
|
87 |
expectations. |
b1b922
|
88 |
|
PE |
89 |
Note that our use of ``pyramid.testing.setUp()`` and |
|
90 |
``pyramid.testing.tearDown()`` aren't actually necessary here; they are only |
|
91 |
necessary when your test needs to make use of the ``config`` object (it's a |
|
92 |
Configurator) to add stuff to the configuration state before calling the view. |
|
93 |
|
86e187
|
94 |
|
65687f
|
95 |
Extra credit |
b1b922
|
96 |
============ |
PE |
97 |
|
86e187
|
98 |
#. Change the test to assert that the response status code should be ``404`` |
SP |
99 |
(meaning, not found). Run ``py.test`` again. Read the error report and see |
|
100 |
if you can decipher what it is telling you. |
b1b922
|
101 |
|
86e187
|
102 |
#. As a more realistic example, put the ``tests.py`` back as you found it, and |
SP |
103 |
put an error in your view, such as a reference to a non-existing variable. |
|
104 |
Run the tests and see how this is more convenient than reloading your |
|
105 |
browser and going back to your code. |
b1b922
|
106 |
|
PE |
107 |
#. Finally, for the most realistic test, read about Pyramid ``Response`` |
86e187
|
108 |
objects and see how to change the response code. Run the tests and see how |
SP |
109 |
testing confirms the "contract" that your code claims to support. |
b1b922
|
110 |
|
PE |
111 |
#. How could we add a unit test assertion to test the HTML value of the |
|
112 |
response body? |
|
113 |
|
|
114 |
#. Why do we import the ``hello_world`` view function *inside* the |
|
115 |
``test_hello_world`` method instead of at the top of the module? |
|
116 |
|
2033ee
|
117 |
.. seealso:: See also :ref:`testing_chapter` |