Introduction to Testing¶
This introduction is specifically aimed at developers new to testing or new to testing in python. Say we have a python
module called test_guide
containing a file base.py
with a class called Resource
in it that has some methods
on it.
Code to test¶
class Resource(object):
"""
A base class for API resources
"""
...
...
def build_param_string(self, params):
"""
This is a simple helper method to build a parameter string. It joins
all list elements that evaluate to ``True`` with an ampersand, '&'
.. code-block:: python
>>> parameters = Resource().build_param_string([
... 'filter[name]=dev', None, 'page=1'
... ])
...
>>> print parameters
filter[name]=dev&page=1
:type params: list
:param params: The parameters to build a string with
:rtype: str
:return: The compiled parameter string
"""
return '&'.join([p for p in params if p])
Setup¶
We first need a tests
directory in our module with a file called base_tests.py
in it. Our example structure
looks like this:
$ ls -alR tests_guide
total 24
drwxr-xr-x 6 ubuntu ubuntu 204 Jul 22 10:39 .
drwxr-xr-x 24 ubuntu ubuntu 816 Jul 22 10:36 ..
-rw-r--r-- 1 ubuntu ubuntu 47 Jul 22 10:05 __init__.py
-rw-r--r-- 1 ubuntu ubuntu 1520 Jul 22 10:39 base.py
drwxr-xr-x 4 ubuntu ubuntu 136 Jul 22 10:45 tests
-rw-r--r-- 1 ubuntu ubuntu 19 Jul 22 10:05 version.py
./tests:
total 8
drwxr-xr-x 4 ubuntu ubuntu 136 Jul 22 10:45 .
drwxr-xr-x 6 ubuntu ubuntu 204 Jul 22 10:39 ..
-rw-r--r-- 1 ubuntu ubuntu 0 Jul 22 10:05 __init__.py
-rw-r--r-- 1 ubuntu ubuntu 125 Jul 22 10:05 base_tests.py
Writing an example test¶
Now we want to test the build_param_string()
method on our Resource
class. Since we only want the results that
evaluate to True
in the method, we need to test it with a bunch of values that evaluate to False
.
from datetime import time
import unittest
from tests_guide.base import Resource
class ResourceTests(unittest.TestCase):
def test_build_param_string(self):
"""
Tests .build_param_string() returns the correct string
"""
resource = Resource()
test_params = [
False,
'filter[name]=dev',
None,
'page=1',
0,
'',
[],
{},
time(0)
]
param_str = resource.build_param_string(test_params)
self.assertEqual(param_str, 'filter[name]=dev&page=1')
Did you see what happened? We first have a test class to group our tests of the Resource
class. Then we have a
method test_build_param_string()
that actually runs the tests. Tests in python are assertions, and the builtin
TestCase has a lot of helpful assertions. For this test, we simply assert that our method’s output is equal to the
output we expect by calling assertEqual()
. We could just as easily used the built in assert
keyword like so:
assert param_str == 'filter[name]=dev&page=1'
If our method’s output had been different than what we expected, our test would have raised an
AssertionError
and the test would be marked as Failed.
Running the tests¶
In all of the Ambition python templates a testing framework is built in and there is a Contributing section with instructions on how to run the tests. It may differ slightly depending on if the project is a Django app or a pure Python project.
If the project is pure Python, you can run:
$ python setup.py nosetests
If the project is a Django app or Django project:
$ python setup.py test
Example output:
$ python setup.py test
running test
running egg_info
writing top-level names to ambition_py_tests_guide.egg-info/top_level.txt
writing ambition_py_tests_guide.egg-info/PKG-INFO
writing requirements to ambition_py_tests_guide.egg-info/requires.txt
writing dependency_links to ambition_py_tests_guide.egg-info/dependency_links.txt
reading manifest file 'ambition_py_tests_guide.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'ambition_py_tests_guide.egg-info/SOURCES.txt'
running build_ext
nosetests tests_guide --verbosity=1
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Destroying test database for alias 'default'...