This repository contains a Django project that demonstrates some nifty Django testing tricks. It was part of a presentation for the Pittsburgh Code & Supply Django meet-up, June 9, 2015. It was written by Jesse Legg (jesse.legg at gmail or @jesselegg).
All code is written against Django 1.8 and Python 3, but much of it should work on earlier versions of Django and Python 2.
testrick.apps.kittens
contains a Django app that provides an interface
to Reddit Kittens (Kittens-as-a-Service, if you will). There are two views:
- show-a-kitten: Returns a random kitten image from Reddit's /awww subreddit
- email-a-kitten: Retrieves a random kitten image and emails it to someone
This app is designed as a basis to demonstrate some testing techniques with Django and is not an actual SaaS business.
Nose is nicer testing for Python.
It provides extensions to the built-in unittest
module that make testing
easier and more fun.
Django-nose is a project that allows you to use nose as the test runner for your Django apps. It offers some very useful features when it comes to testing Django:
- Defaults to testing just your project's apps
- Eliminates the need to import tests into tests/init.py
- Allows you to use nose plugins
- Bundling of Django fixtures for faster loading
- Reuse of Django test databases between test runs
Django-nose also adds the ability to run your Django tests through coverage.py
by simply adding --with-coverage
to the manage.py test
command.
In this project/presentation, we're simple using Django-nose as our test runner, but not exploring all that it can do (that is left as an exercise for the attendee).
This is a very simple Django app that we'll use for demo purposes. It has two simple views and some templates. The first view is mounted at /kitten/. It simply queries the Reddit API and searches the /r/Awww subreddit for 'kitten'. It retrieves the list of those that have thumbnails and randomly selects one for presentation to the user in the Django template.
The second view is at /email-kitten/. This view is similar to the first view, except that it prompts for an email address and then emails the kitten to the email address given.
These views will be the basis for demonstrating Django testing techniques.
The only change is to add django_nose
and coverage
to the project's
requirements.txt file. Django-nose is a Django test runner for the Nose
Python testing library. Nose is nicer testing for Python (see above).
This implements our first unit test. This unit test demonstrates a neat
feature of Django: the mail.outbox
. This is useful for collecting sent
emails so that you can verify subject lines, senders, mail headers and
even content in your automated emails.
These tests live in testtrick.apps.kittens.tests.tests
(sorry for the
redundant naming).
To run tests, simply issue the command python manage.py test
with an
active virtualenv that contains the dependencies listed in requirements.txt.
This adds a test to demonstrate Django's override_settings
decorate. This
allows you to dynamically modify the project's settings configuration during
an individual test run. The override_setting
decorator is documented here:
https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.override_settings
This adds unit tests using the HTTPretty library to mock HTTP responses
in the reddit API. This version fails, however, because the praw
library
was used to interface with reddit's API. This library appears to have
compatibility problems with HTTPretty. The praw
library is removed in
part 5b.
This rewrites the get_a_kitten
function in testtrick.apps.kittens.views
so that it uses pure requests
library calls and not the Reddit wrapper API
module (praw
). This allows tests to pass and successfully mocks out the
HTTP response.
Generalized approach to mocking things using Python's unittest.mock
modules.
Adds a unit test that demonstrates the use of patch
and MagicMock
to
mock out Django's send_mail
function.
NOTE: The mock module was added to Python's standard library in Python 3. If you are using Python 2, you will need to use a third party mock library. A list of such libraries is available here: https://wiki.python.org/moin/PythonTestingToolsTaxonomy#Mock_Testing_Tools