Djangocon: pytest helps you write better django apps - Andreas Pelme

Tags: djangocon, django

(One of the summaries of a talk at the 2014 djangocon.eu).

Andreas Pelme’s slides are at http://speakerdeck.com/pelme .

Django already provides some nice tools to make testing easier. The testing experience can become even better, for instance by using pytest. Pytest is quite popular. The ones using it don’t want to use anything else. (He showed a couple of tweets of well-known django developers to prove it).

Pytest is exensible. Andreas is the maintainer of the pytest-django plugin.

The main advantage of pytest is that it allows you to write pythonic tests, without boilerplate.

  • No more self.assertEquals(a, b) but simply assert a == b.

    Advantage: the error message when it doesn’t match is much clearer than the regular test exception.

  • You don’t need to subclass from TestCase anymore. Just a function is enough. Django’s TestCase would normally give you self.client. The function will look something like def test_something(client): .... The django extension for pytest recognizes “client” and passes in the correct object automatically.

    That auto-recognized “client” attribute? Pytest calls that a “fixture”. No, that’s something else than django’s fixtures.

    Test fixtures are very handy for test dependencies.

You run the tests with py.test. For this, you need to have your project on the python path. So you need to do pip install -e ., so you need to have a setup.py.

You need to tell pytest which tests to run. For this, add something into pytest.ini:

[pytest]
DJANGO_SETTINGS_MODULE=settings

Note: regular django testcases still work! No problem. So you can get started right away without immediately needing to re-write all your existing tests.

Sometimes you don’t need to run all the tests. You can pass a directory or a testfilename to py.test, only those tests will be run, then.

Test organization. You can group tests into directories. By default, it looks for tests named test_*.py. You can change this.

Suggestion: split your tests into separate directories per kind of test. A directory for unit tests. One for integration tests. One for browser tests. It is up to you, though. It depends on the work you’re doing.

Speed: you can tell pytest to reuse the database from the last test run. Of course this is not a good idea when you’re making many model changes. It might be good if you often run a single test (couple of milliseconds) and if it takes a few seconds to set up your full database.

Look at pytest-xdist. It can run your tests in multiple processes. On a 4-processor machine, this cuts your testing time in half! Theres’ also an option for running tests on multiple separate machines.

Before you can use the django database in a test, you first have to mark the test with a decorator. If not, you’re not allowed to hit the database. This is useful for quick unittests. You can tell pytest to run all the tests that don’t require a database: these af often much faster to run.

He then demoed how to create a fixture. His example was one that returned a selenium Firefox webdriver to the test. You can configure a “scope”. With scope='session' you re-use the same object for all the tests. In some cases, this is handy. In other cases you need to limit the scope to an individual test run.

The test would look something like this:

def test_hello(live_server, firefox_driver):
    ....

You simply request the features you need in your test. Simple!

Note: when creating database objects, use factory_boy instead of doing it by hand.

Want to know more? Look at Holger Krekel’s talk on youtube.

http://reinout.vanrees.org/images/2014/django7.jpg

My son admiring a French locomotive in 2007. Vallée de la Doller.

 
vanrees.org logo

About me

My name is Reinout van Rees and I work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):