207 votes

Comment puis-je appeler une commande Django manage.py personnalisée directement à partir d'un pilote de test ?

Je veux écrire un test unitaire pour une commande Django manage.py qui effectue une opération backend sur une table de base de données. Comment pourrais-je invoquer la commande manage directement à partir du code ?

Je ne veux pas exécuter la commande sur le shell du système d'exploitation à partir de tests.py car je ne peux pas utiliser l'environnement de test mis en place à l'aide de manage.py test (base de données de test, boîte aux lettres électronique factice de test, etc...).

372voto

Alex Koshelev Points 5522

La meilleure façon de tester ce genre de choses est d'extraire la fonctionnalité nécessaire de la commande elle-même vers une fonction ou une classe autonome. Cela permet de faire abstraction des "trucs d'exécution de la commande" et d'écrire des tests sans exigences supplémentaires.

Mais si, pour une raison quelconque, vous ne pouvez pas découpler la logique de la commande, vous pouvez l'appeler à partir de n'importe quel code comme ceci :

from django.core.management import call_command

call_command('my_command', 'foo', bar='baz')

20 votes

+1 pour mettre la logique testable ailleurs (méthode de modèle ? méthode de gestionnaire ? fonction autonome ?) de sorte que vous n'ayez pas besoin de jouer avec la machinerie call_command du tout. Cela rend également la fonctionnalité plus facile à réutiliser.

38 votes

Même si vous extrayez la logique, cette fonction est toujours utile pour tester le comportement spécifique de votre commande, comme les arguments requis, et pour s'assurer qu'elle appelle la fonction de votre bibliothèque qui fait le vrai travail.

0 votes

Le premier paragraphe s'applique cualquier situation limite. Déplacez votre propre code de logique commerciale hors du code qui est contraint de s'interfacer avec quelque chose, comme un utilisateur. Cependant, si vous écrivez la ligne de code, elle peut contenir un bogue, et les tests doivent donc être effectués derrière n'importe quelle limite.

25voto

Nate Points 1373

Plutôt que de faire l'astuce call_command, vous pouvez exécuter votre tâche en faisant :

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)

11 votes

Pourquoi faire cela alors que call_command permet également de capturer stdin, stdout, stderr ? Et quand la documentation spécifie la bonne façon de procéder ?

22 votes

C'est une très bonne question. Il y a trois ans, j'aurais peut-être eu une réponse à vous donner ;)

1 votes

Idem pour Nate - lorsque sa réponse était celle que j'avais trouvée il y a un an et demi, je me suis contenté de la compléter...

21voto

Artur Barseghyan Points 439

Le code suivant :

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

...est égal aux commandes suivantes tapées dans le terminal :

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

13voto

zonky Points 381

Voici le lien vers la documentation officielle de django, où ce problème est décrit : https://docs.djangoproject.com/en/1.3/ref/django-admin/#running-management-commands-from-your-code

2voto

Danny Staple Points 1725

En me basant sur la réponse de Nate, j'ai ceci :

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

Utilisation :

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

L'avantage ici est que si vous avez utilisé des options supplémentaires et OptParse, cela va régler le problème pour vous. Ce n'est pas tout à fait parfait - et il ne pipe pas encore les sorties - mais il utilisera la base de données de test. Vous pouvez alors tester les effets de la base de données.

Je suis sûr que l'utilisation du module fantaisie de Michael Foord et le recâblage de la sortie de données pendant la durée d'un test vous permettraient de tirer davantage parti de cette technique - tester la sortie, les conditions de sortie, etc.

5 votes

Pourquoi se donner tout ce mal au lieu d'utiliser simplement call_command ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X