webentwicklung-frage-antwort-db.com.de

Wie kann ich einen benutzerdefinierten Befehl Django manage.py direkt von einem Testtreiber aus aufrufen?

Ich möchte einen Komponententest für einen Befehl Django= manage.py schreiben, der eine Back-End-Operation für eine Datenbanktabelle ausführt. Wie würde ich den Verwaltungsbefehl direkt aus dem Code aufrufen?

Ich möchte den Befehl in der Shell des Betriebssystems nicht über "tests.py" ausführen, da ich die mit "manage.py test" eingerichtete Testumgebung (Testdatenbank, Test-Dummy-E-Mail-Postausgang usw.) nicht verwenden kann.

158
MikeN

Der beste Weg, um solche Dinge zu testen - extrahieren Sie die benötigte Funktionalität aus dem Befehl selbst in eine eigenständige Funktion oder Klasse. Es hilft, von "Command Execution Stuff" zu abstrahieren und Tests ohne zusätzliche Anforderungen zu schreiben.

Aber wenn Sie aus irgendeinem Grund den logischen Formularbefehl nicht entkoppeln können, können Sie ihn von jedem Code aus aufrufen, indem Sie die folgende call_command -Methode verwenden:

from Django.core.management import call_command

call_command('my_command', 'foo', bar='baz')
287
Alex Koshelev

Anstatt den Trick call_command auszuführen, können Sie Ihre Aufgabe folgendermaßen ausführen:

from myapp.management.commands import my_management_task
cmd = my_management_task.Command()
opts = {} # kwargs for your command -- lets you override stuff for testing...
cmd.handle_noargs(**opts)
21
Nate

den folgenden Code:

from Django.core.management import call_command
call_command('collectstatic', verbosity=3, interactive=False)
call_command('migrate', 'myapp', verbosity=3, interactive=False)

... entspricht den folgenden im Terminal eingegebenen Befehlen:

$ ./manage.py collectstatic --noinput -v 3
$ ./manage.py migrate myapp --noinput -v 3

Siehe Ausführen von Verwaltungsbefehlen aus Django docs .

16

In der Django-Dokumentation im call_command wird nicht erwähnt, dass out nach sys.stdout Umgeleitet werden muss. Der Beispielcode sollte lauten:

from Django.core.management import call_command
from Django.test import TestCase
from Django.utils.six import StringIO
import sys

class ClosepollTest(TestCase):
    def test_command_output(self):
        out = StringIO()
        sys.stdout = out
        call_command('closepoll', stdout=out)
        self.assertIn('Expected output', out.getvalue())
10
Alan Viars

Aufbauend auf Nates Antwort habe ich Folgendes:

def make_test_wrapper_for(command_module):
    def _run_cmd_with(*args):
        """Run the possibly_add_alert command with the supplied arguments"""
        cmd = command_module.Command()
        (opts, args) = OptionParser(option_list=cmd.option_list).parse_args(list(args))
        cmd.handle(*args, **vars(opts))
    return _run_cmd_with

Verwendung:

from myapp.management import mycommand
cmd_runner = make_test_wrapper_for(mycommand)
cmd_runner("foo", "bar")

Dies hat den Vorteil, dass Sie zusätzliche Optionen und OptParse verwenden, um dies für Sie zu erledigen. Es ist nicht ganz perfekt - und es leitet noch keine Ausgaben weiter - aber es wird die Testdatenbank verwenden. Sie können dann auf Datenbankeffekte testen.

Ich bin mir sicher, dass die Verwendung des Micheal Foords-Scheinmoduls und die Neuverdrahtung von stdout für die Dauer eines Tests bedeuten würde, dass Sie auch mehr aus dieser Technik herausholen könnten - testen Sie die Ausgabe, die Ausgangsbedingungen usw.

1
Danny Staple