73

I created custom django-admin commands

But, I don't know how to test it in standard django tests

dixon
  • 1,265
  • 2
  • 9
  • 10

6 Answers6

127

If you're using some coverage tool it would be good to call it from the code with:

from django.core.management import call_command
from django.test import TestCase

class CommandsTestCase(TestCase):
    def test_mycommand(self):
        " Test my custom command."

        args = []
        opts = {}
        call_command('mycommand', *args, **opts)

        # Some Asserts.

From the official documentation

Management commands can be tested with the call_command() function. The output can be redirected into a StringIO instance

Jorge E. Cardona
  • 92,161
  • 3
  • 37
  • 44
  • 7
    Note the How to covers testing https://docs.djangoproject.com/en/1.9/howto/custom-management-commands/#testing – Harry Moreno Jul 01 '16 at 18:32
  • 2
    The above link is broken, access the latest version of mentioned doc [here](https://docs.djangoproject.com/en/dev/howto/custom-management-commands/#testing). – artu-hnrq Sep 17 '21 at 01:27
  • 1
    My command creates entries in the database, do you know how to use the testdb ? – SWater Jan 24 '23 at 13:11
  • 2
    @SWater Hi, as far as I understand, the `TestCase` from `django.test` uses the test database, see [here](https://docs.djangoproject.com/en/4.1/topics/testing/overview/#the-test-database) – Jorge E. Cardona Jan 25 '23 at 08:59
24

You should make your actual command script the minimum possible, so that it just calls a function elsewhere. The function can then be tested via unit tests or doctests as normal.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • 8
    Not sure I agree with this. If I could do that, I probably would not make it a Django command. I would just run it as a python program. The reason I make Django commands is to get all the Django infrastructure. – Chuck Feb 20 '14 at 18:02
  • Well, sometimes you can't just run it as a python program because of the import issues. – Pavel Vergeev Jan 23 '18 at 08:22
20

you can see in github.com example see here

def test_command_style(self):
    out = StringIO()
    management.call_command('dance', style='Jive', stdout=out)
    self.assertEquals(out.getvalue(),
        "I don't feel like dancing Jive.")
madjardi
  • 5,649
  • 2
  • 37
  • 37
6

To add to what has already been posted here. If your django-admin command passes a file as parameter, you could do something like this:

from django.test import TestCase
from django.core.management import call_command
from io import StringIO
import os


class CommandTestCase(TestCase):
    def test_command_import(self):
        out = StringIO()
        call_command(
            'my_command', os.path.join('path/to/file', 'my_file.txt'),
            stdout=out
        )
        self.assertIn(
        'Expected Value',
            out.getvalue()
        )

This works when your django-command is used in a manner like this:

$ python manage.py my_command my_file.txt
asfor
  • 61
  • 1
  • 2
-1

A simple alternative to parsing stdout is to make your management command exit with an error code if it doesn't run successfully, for example using sys.exit(1).

You can catch this in a test with:

    with self.assertRaises(SystemExit):
        call_command('mycommand')
Bosco
  • 935
  • 10
  • 18
-3

I agree with Daniel that the actual command script should do the minimum possible but you can also test it directly in a Django unit test using os.popen4.

From within your unit test you can have a command like

fin, fout = os.popen4('python manage.py yourcommand')
result = fout.read()

You can then analyze the contents of result to test whether your Django command was successful.

Aaron Vernon
  • 268
  • 2
  • 10
  • 7
    don't use subprocesses for this kind of testing, and certainly don't use os.popen4, if you really wanted to do that you'd use the subprocess package. – Chris Withers Jan 07 '15 at 14:32