Désactiver les migrations lors de l'exécution de tests unitaires dans Django 1.7


110

Django 1.7 a introduit les migrations de bases de données .

Lors de l'exécution des tests unitaires dans Django 1.7, cela force une migration , qui prend beaucoup de temps. Je voudrais donc ignorer les migrations de django et créer la base de données dans l'état final.

Je sais qu'ignorer les migrations peut être une mauvaise pratique, car cette partie du code ne serait pas testée. Mais ce n'est pas le cas: j'exécute les migrations complètes dans le serveur de test CI (jenkins). Je veux seulement sauter les migrations dans mes tests locaux, où la vitesse compte.


Un peu de contexte:

Jusqu'à Django 1.6 , lors de l'utilisation de South, j'ai utilisé le paramètre SOUTH_TESTS_MIGRATE :

Par défaut, la commande syncdb de South appliquera également les migrations si elle est exécutée en mode non interactif, ce qui inclut le moment où vous exécutez des tests - elle exécutera chaque migration à chaque fois que vous exécutez vos tests.

Si vous voulez que le testeur utilise syncdb au lieu de migrer - par exemple, si vos migrations prennent beaucoup trop de temps à appliquer - définissez simplement SOUTH_TESTS_MIGRATE = False dans settings.py.

Cependant, syncdb n'existe plus, maintenant il migre .

Et à partir de Django 1.8, j'utiliserai le paramètre --keepdb :

L'option --keepdb peut être utilisée pour conserver la base de données de test entre les exécutions de test. Cela a l'avantage de sauter à la fois les actions de création et de destruction, ce qui réduit considérablement le temps d'exécution des tests, en particulier ceux d'une grande suite de tests. Si la base de données de test n'existe pas, elle sera créée lors de la première exécution, puis conservée pour chaque exécution suivante. Toutes les migrations non appliquées seront également appliquées à la base de données de test avant d'exécuter la suite de tests.

Cette question est donc limitée à Django 1.7.


Je dirais que pendant UT, vous n'exécutez vraiment pas les migrations d'une manière qui les teste, car la base de données avec laquelle vous commencez est inexistante. Le test des migrations ne se produit réellement que lorsque vous migrez une base de données existante. Cette entreprise de migrations 1.7 est la première vraie bavure sous la selle que j'ai eue avec Django, mais c'est vraiment un gros irritant. Au moins, South a obtenu le bon scénario de test pour les migrations.
boatcoder

Le django-test-without-migrationspackage a été vraiment pratique pour moi, vous voudrez peut-être changer la réponse acceptée en stackoverflow.com/a/28993456/200224
Andy

Je préfère éviter d'ajouter de nouvelles dépendances, si possible.
David Arcos

Réponses:


79

Regardez cette solution de contournement , publiée par Bernie Sumption sur la liste de diffusion des développeurs Django:

Si makemigrations n'a pas encore été exécuté, la commande "migrate" traite une application comme non migrée et crée des tables directement à partir des modèles, tout comme syncdb l'a fait dans 1.6. J'ai défini un nouveau module de paramètres uniquement pour les tests unitaires appelé "settings_test.py", qui importe * depuis le module de paramètres principal et ajoute cette ligne:

MIGRATION_MODULES = {"myapp": "myapp.migrations_not_used_in_tests"}

Ensuite, je lance des tests comme celui-ci:

DJANGO_SETTINGS_MODULE = "myapp.settings_test" test python manage.py

Ces imbéciles pensent que l'application n'est pas migrée et que chaque fois qu'une base de données de test est créée, elle reflète la structure actuelle de models.py.

Dans Django 1.9, cette situation est quelque peu améliorée et vous pouvez définir la valeur sur None:

MIGRATION_MODULES = {"myapp": Aucun}


9
Notez que le myapp.migrations_not_used_in_testsmodule ne doit pas exister.
bmihelac le

4
En plus du commentaire @bmihelac fait sur le module inexistant, la chaîne du module doit contenir la sous-chaîne 'migrations', pour savoir pourquoi voir: github.com/django/django/blob/stable/1.7.x/django/db/migrations /…
nealtodd

7
Un résumé d'une fonction pour construire MIGRATION_MODULES dynamiquement dans settings_test.py: gist.github.com/nealtodd/2869341f38f5b1eeb86d
nealtodd

1
TY. J'ai pu réduire mes tests unitaires de 13 secondes à 4 secondes grâce à cela. En outre, plus de gains de vitesse peuvent être trouvés en utilisant sqlite pour les tests. Pour moi, utiliser postgres pour les tests prend 5,5 secondes, mais sqlite prend 4 secondes.
Gattster

21
D'après les commentaires de l'essentiel de @ nealtodd, voici un lien vers une solution qui évite certains des pièges et qui est super simple: gist.github.com/NotSqrt/5f3c76cd15e40ef62d09
djsutho

72

Voici la fin de mon fichier de paramètres:

class DisableMigrations(object):

    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None


TESTS_IN_PROGRESS = False
if 'test' in sys.argv[1:] or 'jenkins' in sys.argv[1:]:
    logging.disable(logging.CRITICAL)
    PASSWORD_HASHERS = (
        'django.contrib.auth.hashers.MD5PasswordHasher',
    )
    DEBUG = False
    TEMPLATE_DEBUG = False
    TESTS_IN_PROGRESS = True
    MIGRATION_MODULES = DisableMigrations()

basé sur cet extrait

J'ai désactivé les migrations uniquement lorsque les tests sont en cours d'exécution


1
Agréable! J'ajouterais également une __setitem__(self, *_)méthode car nous avons eu des problèmes avec les applications qui définissent leur propre migration comme settings.MIGRATION_MODULES['chroniker'] = 'db_migrations'
Zhe Li

1
Merci beaucoup pour cela, c'est la seule chose que j'ai trouvée qui fonctionne réellement.
fluffels

Cela ne fonctionne plus dans Django 1.9 lors de l'exécution de tests en mode parallèle. En utilisant des tests non parallèles normaux, cela continue de fonctionner correctement, mais le passage en mode parallèle entraîne des erreurs indiquant que les tables ne sont pas trouvées.
LS55321

@LeeSemel en mode parallèle vous voudrez probablement utiliser la solution de rlmv
Guillaume Vincent

@guillaumevincent J'ai le même problème lors de l'utilisation de django-test-without-migrations en mode parallèle
LS55321


3

Mise à jour : Qu'à cela ne tienne, ce changement a été annulé avant la sortie de la version 1.10 finale. Espérons qu'il reviendra dans une prochaine version.


Notez qu'à partir de Django 1.10, cela peut être contrôlé par un paramètre de base de données de test.

ÉMIGRER

Défaut: True

S'il est défini sur False, Django n'utilisera pas les migrations pour créer la base de données de test.



1

Pour django 1.9 et plus la réponse de Guillaume Vincent ne fonctionne plus, alors voici une nouvelle solution:

J'utilise cet extrait dans mon fichier de paramètres, après la définition du INSTALLED_APPS

if os.environ.get('TESTS_WITHOUT_MIGRATIONS', False):
    MIGRATION_MODULES = {
        app.split('.')[-1]: None for app in INSTALLED_APPS
    }

Il itère sur toutes les applications installées et les marque comme n'ayant pas de module de migration. Consultez la documentation de django pour plus d'informations .

En utilisant cet extrait, vous pouvez exécuter vos tests, en définissant la variable d'environnement TESTS_WITHOUT_MIGRATIONS, par exemple:

TESTS_WITHOUT_MIGRATIONS=1 ./manage.py test

1

Je viens de comprendre comment désactiver les migrations après django 1.10, cela pourrait peut-être aider quelqu'un. Voici le lien sur git

class DisableMigrations(dict):
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None

DATABASES = DisableMigrations()

MIGRATION_MODULES = DisableMigrations()

Les migrations pour django 1.10 ont deux parties, veuillez regarder load_disk et recorder

La partie du load_diskmodèle d'application pour les migrations qui sera ajouté à INSTALL_APP Et la partie de la recorderconnexion à la base de données Pour la version antérieure à la version 1.9, nous devons définir MIGRATION_MODULES={'do.not.migrate':'notmigrations'}lorsque vous exécutez le test Maintenant, nous devons la définir Aucune comme MIGRATION_MODULES={'do.not.migrate':None} Donc si nous ne voulons pas effectuer de migrations pour aucune application , étendez simplement un dict et retournez Nonepour getitemfonction, et faites de même à DATABASES, c'est la bonne chose à faire

PS: pour la commande, vous devez spécifier --setting=module.path.settings_test_snippetaprès test PPS Si vous travaillez avec pycharm, ne définissez pas d'--settings options sur Run/Debug configurations, ajoutez simplement le chemin desettings_test_snippet.py au paramètre personnalisé. Ça va bien !!

prendre plaisir

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.