DEV Community

Adam Parkin
Adam Parkin

Posted on • Originally published at codependentcodr.com on

Python manage.py pytest or Making Django Use Pytest

This post was originally posted on Apr 18, 2020 to https://www.codependentcodr.com/python-managepy-pytest-or-making-django-use-pytest.html#python-managepy-pytest-or-making-django-use-pytest

Edit: For those who prefer visual content, I've recorded this as a video on Youtube which you can find at: https://youtu.be/toKW2YLnGFQ

Ok, so I've had to convert some Django projects to use Pytest as the test runner rather than the built in one that Django uses. This is actually pretty straightforward, and I even recorded a Youtube video showing the process.

That's all fine and good, but one of the complaints I've heard from Django-ista's (is that a term? Djangoites? Django Devotees?) is that it means now the good old normal python manage.py test no longer works.

So challenge accepted, as one can certainly create custom manage.py commands in Django.

Python Manage.py pytest

So first challenge is "how do we run pytest from Python?" as normally you run Pytest as a command line tool. As it turns out there's docs on how to do this on Pytest's site.

The trick is to import pytest and then call pytest.main() passing in the same command line arguments you'd give to Pytest in the terminal to thatfunction. As an example:

pytest.main(['--lf'])
Enter fullscreen mode Exit fullscreen mode

Would be the same thing as doing pytest --lf on the command-line. Easy peasy.So started a basic custom management command in a file called pytest.py and put it into my Django project's management/commands directory.

import pytest
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "Runs tests with Pytest"

    def handle(self, \*args, \*\*options):
        pytest.main()
Enter fullscreen mode Exit fullscreen mode

This works, in that now I can do python manage.py pytest and it'll run Pytest as if I just ran the pytest executable in the current directory.

Cool, but how do I start passing arguments? Normally in a custom Django management command you define a add_arguments function and use the argparsemodule to define the expected arguments for your custom command. In this case though, I essentially want the interface to Pytest, which would be non-trivial to recreate by hand (there's a lot of options on that Pytest executable).

But, with argparse, there is a way to essentially say "accept any arguments",and that's the argparse.REMAINDER value for the nargsparameter.

argparse.REMAINDER. All the remaining command-line arguments are gathered into a list. This is commonly useful for command line utilities that dispatch to other command line utilities

Perfect, that's exactly what I want. Adding to our management command is straightforward:

import argparse
import pytest
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "Runs tests with Pytest"

    def add\_arguments(self, parser):
        parser.add\_argument("args", nargs=argparse.REMAINDER)

    def handle(self, \*args, \*\*options):
        pytest.main(list(args))
Enter fullscreen mode Exit fullscreen mode

Now whatever arguments we pass to our manage.py pytest command will be passed directly through to the pytest.main function. Exactly what I want, and super concise.

But That's Still Different

At this point it worked, but I could still hear those nagging voices saying,"yeah but manage.py pytest is not the same as manage.py test". Fine, as it turns out though you can override any of the built-in manage.py commands.

The trick is to just create a custom management command with the name of the command you want to override, and make sure your app is the last one in Django'sINSTALLED_APPS setting. So in our case we could just rename ourmanagement/pytest.py file to management/test.py and it'd work. But I kindaliked having both (ie both manage.py pytest and manage.py test being effectively an alias to it). So I created a management/test.py file and put in the following:

# Override the built in Django manage.py test with pytest
from djangotest.management.commands.pytest import Command
Enter fullscreen mode Exit fullscreen mode

Yup, that's it, 1 line of code. Now doing a python manage.py test runs Pytest as the test runner.

Did this all on a test project, source is up on Github at:https://github.com/pzelnip/djangotest

Or if you just want to see the management commands, they're at:https://github.com/pzelnip/djangotest/tree/master/djangotest/management/commands

Top comments (1)

Collapse
 
bp100a profile image
Harry Collins

I'm trying this out with Django 3.0 and there are two things I notice:

  • manage.py won't pass-thru any arguments not spelled out
  • the command arguments passed to pytest are stripped of -- and - prefix, and pytest seems to want these.