DB migration support has been added in Django 1.7+, superseding South. More specifically, it's possible to automatically generate migrations steps when one or more changes in the application models are detected. Definitely a nice feature!
I've written a small generic unit-test that one should be able to drop into the tests directory of any Django project and that checks there's no pending migrations, ie. if the models are correctly in sync with the migrations declared in the application. Handy to check nobody has forgotten to git add the migration file or that an innocent looking change in models.py doesn't need a migration step generated. Enjoy!
You can test how your django app behaves depending on what kind of response it gets from the API. It assumes were're using the python requests library.
Ever tried to unit test custom fields or abstract models? If so, you probably used a solution like [this one](http://djangosnippets.org/snippets/2843/). It surely works, but it has some issues:
1. Runs 'syncdb' several times.
2. It's not automatic. You must add the mixin or copy the code to each of the TestCases.
This test runner adds to INSTALLED_APPS the 'app.tests' package for **each of the specified** apps, **before the 'syncdb' happens**.
This has the [discovery runner](https://pypi.python.org/pypi/django-discover-runner) as a dependency (for 1.5 only, it will be the default in 1.6). However, it comes as a mixin so it should be easily pluggable to other test runners.
If you intend to use the mixin with other runners, note that it imports 'app.tests.models' so it won't work with tests modules (tests.py). Your runner must "use" test packages (like discovery runner).
# USAGE
# in your settings.py:
TESTAPPS_INSTALL = (
'app1',
'app2',
# ...
)
TEST_RUNNER = 'testapp_runner.DiscoverTestAppRunner' # example import path
# extra apps in command line.
# run test for apps 'app1' to 'app4', adding 'app3' and 'app4' to the TESTAPPS_INSTALL setting.
manage.py test app1 app2 app3 app4 --with-test-app=app3 --with-test-app=app4
This code allows you to register a model to Django that is only used for unit testing.
It will not exist in the regular Django workflow. After the tests executed, the Django settings are restored.
Usage:
1. Change `tests.py` into a `tests` package.
2. Place a `models.py` in the `tests` package.
3. Use the following code below to enable it.
Example:
class MyTest(CustomSettingsTestCase):
new_settings = dict(
INSTALLED_APPS=(
'django.contrib.contenttypes',
'django.contrib.auth',
'app_to_test',
'app_to_test.tests',
)
)
Based on http://djangosnippets.org/snippets/1011/ as Django 1.4 version
This is hardcoded to use [django-discover-runner](http://pypi.python.org/pypi/django-discover-runner) since that's my main test runner but could easily be adopted to use Django's own test runner. If you're using a terminal that is capable of showing 256 colors use the `Terminal256Formatter` formatter instead.
Enabled it with the `TEST_RUNNER` setting:
TEST_RUNNER = 'dotted.path.to.highlighted.runner.HighlightedDiscoverRunner'
Where `dotted.path.to.highlighted.runner` is the Python import path of the file you saved the runner in.
When debugging tests you frequently need to inspect response content, making a pdb. set_trace() breakpoint and printing response.content
but html isn't enough human readable (even for programmers :D) so, why not open it in your browser? Suposse you save this code in utils.py and you break your testcase as this:
response = self.client.get(self.url)
import pdb; pdb.set_trace()
Then:
(pdb) from utils import load_response_on_firefox
(pdb) load_response_on_firefox(response)
Ta-Da!
This custom test suite runner will record all of the URLs accessed during your test suite. It will compare it to the list of all URLs you have configured for your site and produce a report of any URLs missed. It requires that all URLs are named (using the ``name=`` parameter).
To use is, set the ``TEST_RUNNER`` variable in your configuration to this class. You can also define ignored URLs. For example, to filter out the admin URLs, you can use:
IGNORED_COVERAGE_URLS = ['^admin/',
'^admin/doc/']
Django 1.3 has an assertNumQueries method which will allows you to simply specify the number of queries you expect. Sometimes, however, specifying the exact number of queries is overkill, and makes the test too brittle. This code provides a way to make more forgiving tests.
See http://lukeplant.me.uk/blog/posts/fuzzy-testing-with-assertnumqueries/
This is an update to Simon Willison's snippet http://djangosnippets.org/snippets/963/, along with one of the comments in that snippet.
This class lets you create a Request object that's gone through all the middleware. Suitable for unit testing when you need to modify something on the request directly, or pass in a mock object.
(note: see http://labix.org/mocker for details on how to mock requests for testing)
Example on how to use:
from django.test import TestCase
from yourapp import your_view
from yourutils import RequestFactory
class YourTestClass(TestCase):
def setUp(self):
pass
def tearDown(self):
pass
# Create your own request, which you can modify, instead of using self.client.
def test_your_view(self):
# Create your request object
rf = RequestFactory()
request = rf.get('/your-url-here/')
# ... modify the request to your liking ...
response = your_view(request)
self.assertEqual(response.status_code, 200)
Suggestions/improvements are welcome. :)
This allows you to exclude certain apps when doing standard tests (manage.py test) by default. You set the settings/local_settings variable EXCLUDE_APPS and it will exclude those apps (like django, registration, south... etc). This makes running tests much faster and you don't have to wait for a bunch of tests you don't care about (per say).
You can override it by adding the app to the command line still. So if 'south' is in the excluded apps you can still run:
'python manage.py test south'
and it will run the south tests.
You will also need to tell django to use this as the test runner:
TEST_RUNNER = 'testing.simple.AdvancedTestSuiteRunner'
So you need to change some settings when running an individual test in a test case. You could just wrap the test between `old_value = settings.MY_SETTING` and `settings.MY_SETTING = old_value`. This snippet provides a helper which makes this a bit more convenient, since settings are restored to their old values automatically.
Example usage:
class MyTestCase(TestCase):
def test_something(self):
with patch_settings(MY_SETTING='my value',
OTHER_SETTING='other value'):
do_my_test()