nigel222
nigel222

Reputation: 8192

Why isn't call_command( ... stdout=f ) intercepting stdout?

Help! I am unable to get testing for my management command to work. The command works fine when tested manually:

$ ./manage.py import_stock stock/tests/header_only.csv  
Descriptions: 0 found, 0 not found, 0 not unique
StockLines:   0 found, 0 not found, 0 not unique

but not in a test. It's outputting to stdout despite call_command specifying stdout=f (f is a StringIO()). Running the test, I get

$ ./manage.py  test stock/tests --keepdb
Using existing test database for alias 'default'...
System check identified no issues (0 silenced).
Descriptions: 0 found, 0 not found, 0 not unique
StockLines:   0 found, 0 not found, 0 not unique
Returned
""

F
======================================================================
FAIL: test_001_invocation (test_import_stock_mgmt_cmd.Test_010_import_stock)
make sure I've got the basic testing framework right!
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/nigel/django/sarah/silsondb/stock/tests/test_import_stock_mgmt_cmd.py",line 32, in test_001_invocation
    self.assertIn('Descriptions: 0', text)                     # do-nothing
AssertionError: 'Descriptions: 0' not found in ''

----------------------------------------------------------------------
Ran 1 test in 0.006s

FAILED (failures=1)
Preserving test database for alias 'default'...

The test code which generates this is as follows. print(f'Returned\n"{text}"') shows that I'm getting a null string back from do_command (which creates the StringIO() and invokes call_command ). What I'm trying to intercept is being written to the console, just as when I invoke the command directly.

import csv
import io

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

class Test_010_import_stock( TestCase):

    def do_command( self, *args, **kwargs):
        with io.StringIO() as f:
            call_command(  *args, stdout=f ) 
            return f.getvalue()

    def test_001_invocation(self):
        """ make sure I've got the basic testing framework right! """

        text = self.do_command( 'import_stock', 'stock/tests/header_only.csv')
        print(f'Returned\n"{text}"')
        print()
        self.assertIn('Descriptions: 0', text)                     # do-nothing
        self.assertIn('Stocklines:   0', text )

Upvotes: 3

Views: 859

Answers (1)

nigel222
nigel222

Reputation: 8192

I was using:
sys.stdout.write()
but should have been using:
self.stdout.write()

(use self instead of sys)

When you are using management commands and wish to provide console output, you should write to self.stdout and self.stderr, instead of printing to stdout and stderr directly. By using these proxies, it becomes much easier to test your custom command. https://docs.djangoproject.com/en/dev/howto/custom-management-commands/

Upvotes: 3

Related Questions