web-dev-qa-db-fra.com

Commande de tests Unittest

Comment être sûr de l'ordre des méthodes les plus strictes? Les préfixes alphabétiques ou numériques sont-ils appropriés?

class TestFoo(TestCase):
    def test_1(self):
        ...
    def test_2(self):
        ...

ou

class TestFoo(TestCase):
    def test_a(self):
        ...
    def test_b(self):
        ...
36
nmb.ten

Vous pouvez le désactiver en définissant sortTestMethodsUsing sur None: http://docs.python.org/2/library/unittest.html#unittest.TestLoader.sortTestMethodsUsing

Pour les unittests purs, vous avez raison; mais pour les tests de composants et les tests d'intégration .... Je ne suis pas d'accord pour dire que vous ne supposez rien à propos de l'état . Que faire si vous testez l'état . Par exemple, votre test valide qu'un service est démarré automatiquement lors de l'installation. Si, dans votre configuration, vous démarrez le service, puis effectuez l'assertion, vous ne testez plus l'état, mais vous testez la fonctionnalité de "démarrage du service".

Un autre exemple est lorsque votre configuration prend beaucoup de temps ou requiert beaucoup d'espace et qu'il devient tout simplement impossible de l'exécuter fréquemment.

De nombreux développeurs ont tendance à utiliser des frameworks "unittest" pour les tests de composants ... alors arrêtez-vous et demandez-vous si je fais des tests unittest ou des composants.

58
max

Il n'y a aucune raison, étant donné que vous ne pouvez pas construire sur ce qui a été fait dans un test précédent ou devriez tout reconstruire à partir de zéro pour le prochain test. Au moins, aucune raison n'est généralement proposée, mais les gens disent simplement avec confiance que "vous ne devriez pas". Ce n'est pas utile.

En général, je suis fatigué de lire trop de réponses ici qui disent essentiellement "vous ne devriez pas faire cela" au lieu de donner des informations sur la meilleure façon de le faire si, de l'avis du demandeur, il y a de bonnes raisons de le faire. Si je voulais que quelqu'un me dise si je devais ou non faire quelque chose, j'aurais demandé si c'est une bonne idée de le faire. 

En dehors de cela, si vous lisez, loadTestsFromTestCase et son nom analysent en fin de compte les méthodes avec un motif de nom dans l'ordre de leur apparition dans le dictionnaire des méthodes de classes, donc dans l'ordre des clés. Il prend cette information et fait une suite de tests pour la mapper à la classe TestCase. En lui donnant une liste ordonnée comme vous le souhaitez, vous pouvez le faire. Je ne suis pas sûr de la manière la plus efficace/la plus propre de le faire, mais cela fonctionne. 

67
Seren Seraph

Si vous utilisez 'nose' et que vous écrivez vos scénarios de test en tant que fonctions (et non en tant que méthodes d'une classe dérivée de TestCase), 'nose' ne modifie pas l'ordre, mais utilise l'ordre des fonctions défini dans le fichier. Afin d'avoir les méthodes assert_ * à portée de main sans avoir besoin de sous-classe TestCase, j'utilise généralement le module de test de numpy. Exemple: 

from numpy.testing import *

def test_aaa():
    assert_equal(1, 1)

def test_zzz():
    assert_equal(1, 1)

def test_bbb():
    assert_equal(1, 1)

Exécuter cela avec '' nosetest -vv '' donne:

test_it.test_aaa ... ok
test_it.test_zzz ... ok
test_it.test_bbb ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.050s
OK

Note à tous ceux qui soutiennent que les tests unitaires ne doivent pas être ordonnés: s'il est vrai que les tests unitaires doivent être isolés et peuvent être exécutés indépendamment, vos fonctions et vos classes ne sont généralement pas indépendantes. Ils construisent plutôt sur un autre, des fonctions les plus simples/de bas niveau aux fonctions plus complexes/de haut niveau. Lorsque vous commencez à optimiser vos fonctions de bas niveau et à tout gâcher (pour ma part, je le fais souvent; sinon, vous n’avez probablement pas besoin de tests unitaires de toute façon ;-) alors c’est beaucoup mieux pour diagnostiquer la cause, lorsque les tests pour les fonctions simples apparaissent en premier et les tests pour les fonctions qui dépendent de ces fonctions ultérieurement. Si les tests sont triés par ordre alphabétique, la cause réelle est généralement noyée parmi cent affirmations échouées, qui n'existent pas car la fonction testée a un bogue, mais parce que la fonction de bas niveau sur laquelle elle s'appuie.

C'est pourquoi je veux que mes tests unitaires soient triés comme je les ai spécifiés: ne pas utiliser l'état qui avait été créé lors des premiers tests lors de tests ultérieurs, mais un outil très utile pour diagnostiquer les problèmes.

12
Elmar Zander

Pourquoi avez-vous besoin d'une commande de test spécifique? Les tests doivent être isolés et il doit donc être possible de les exécuter dans n'importe quel ordre, voire en parallèle.

Si vous devez tester quelque chose comme une désinscription d'utilisateur, le test peut créer une nouvelle base de données avec un abonnement test, puis essayer de vous désabonner. Ce scénario a ses propres problèmes, mais au final, il vaut mieux que les tests dépendent les uns des autres. (Notez que vous pouvez décomposer le code de test commun, de sorte que vous n’aviez pas à répéter le code de configuration de la base de données ni à créer des données de test à la volée.)

12
zoul

Je suis à moitié d’accord avec l’idée que les tests ne devraient pas être commandés. Dans certains cas, il est utile (c'est plus facile que ça!) De les avoir en ordre ... après tout, c'est la raison de l'unité dans UnitTest.

Cela dit, une alternative consiste à utiliser des objets fantaisie pour simuler et à patcher les éléments devant s'exécuter avant le code spécifique testé. Vous pouvez également y insérer une fonction factice pour patcher votre code. Pour plus d’informations, consultez Mock, qui fait maintenant partie de la bibliothèque standard . Mock

Voici quelques vidéos YouTube si vous n'avez jamais utilisé Mock auparavant.

Vidéo 1

Vidéo 2

Vidéo 3

Plus précisément, essayez d’utiliser des méthodes de classe pour structurer votre code, puis placez toutes les méthodes de classe dans une méthode de test principale.

import unittest
import sqlite3

class MyOrderedTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.create_db()
        cls.setup_draft()
        cls.draft_one()
        cls.draft_two()
        cls.draft_three()

    @classmethod
    def create_db(cls):
        cls.conn = sqlite3.connect(":memory:")

    @classmethod
    def setup_draft(cls):
        cls.conn.execute("CREATE TABLE players ('draftid' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'first', 'last')")

    @classmethod
    def draft_one(cls):
        player = ("Hakeem", "Olajuwon")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    @classmethod
    def draft_two(cls):
        player = ("Sam", "Bowie")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    @classmethod
    def draft_three(cls):
        player = ("Michael", "Jordan")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    def test_unordered_one(self):
        cur = self.conn.execute("SELECT * from players")
        draft = [(1, u'Hakeem', u'Olajuwon'), (2, u'Sam', u'Bowie'), (3, u'Michael', u'Jordan')]
        query = cur.fetchall()
        print query
        self.assertListEqual(query, draft)

    def test_unordered_two(self):
        cur = self.conn.execute("SELECT first, last FROM players WHERE draftid=3")
        result = cur.fetchone()
        third = " ".join(result)
        print third
        self.assertEqual(third, "Michael Jordan")
8
Jason Wirth

Ne comptez pas sur la commande. S'ils utilisent un état commun tel que le système de fichiers ou la base de données, vous devez créer les méthodes setUp et tearDown qui placent votre environnement dans un état pouvant être testé, puis nettoyer après l'exécution des tests. Chaque test doit supposer que l’environnement est tel que défini dans setUp et ne doit en aucun cas être supposé.

7
user23743

Il existe un certain nombre de raisons de hiérarchiser les tests, notamment la productivité, pour laquelle JUnit Max est conçu. Il est parfois utile de garder des tests très lents dans leur propre module afin de pouvoir obtenir un retour rapide des tests qui ne souffrent pas des mêmes dépendances lourdes. La commande est également utile pour dépister les échecs des tests qui ne sont pas complètement autonomes.

6
eradman

Ok, peut-être un peu plus tard, mais quand même ...

Vous devriez essayer proboscis library. Cela vous permettra de faire un ordre de test et de configurer toutes les dépendances de test. Je l'utilise et cette bibliothèque est vraiment géniale. 

Par exemple, si test case #1 from module A doit dépendre de test case #3 de module B, vous POUVEZ définir ce comportement en utilisant la bibliothèque.

6
gahcep

http://docs.python.org/library/unittest.html

Notez que l'ordre dans lequel les différents cas de test seront exécutés est déterminé en triant les noms des fonctions de test en fonction de l'ordre intégré des chaînes.

Si vous avez besoin de définir explicitement l'ordre, utilisez un test monolithique. 

class Monolithic(TestCase):
  def step1(self):
      ...

  def step2(self):
      ...

  def steps(self):
    for name in sorted(dir(self)):
      if name.startswith("step"):
        yield name, getattr(self, name) 

  def test_steps(self):
    for name, step in self.steps():
      try:
        step()
      except Exception as e:
        self.fail("{} failed ({}: {})".format(step, type(e), e)

Départ post pour plus de détails.

4
morsik

Il existe des scénarios où l'ordre peut être important et où setUp et Teardown sont limités. Il n'y a qu'une seule méthode setUp et tearDown, ce qui est logique, mais vous ne pouvez y mettre que trop d'informations jusqu'à ce qu'il ne soit pas clair ce que setUp ou tearDown pourrait réellement faire.

Prenez ce test d'intégration à titre d'exemple:

Vous écrivez des tests pour voir si le formulaire d'inscription et le formulaire de connexion fonctionnent correctement. Dans ce cas, la commande est importante car vous ne pouvez pas vous connecter sans compte existant . Plus important encore, l'ordre de vos tests représente une sorte d'interaction utilisateur. Où chaque test peut représenter une étape de l'ensemble du processus ou du flux que vous testez.

La division de votre code en ces éléments logiques présente plusieurs avantages. 

Ce n’est peut-être pas la meilleure solution, mais j’utilise souvent une méthode qui lance les tests. 

def test_registration_login_flow(self):
    _test_registration_flow()
    _test_login_flow()
4
Jonas Geiregat

Une méthode simple pour commander des tests "unittest" consiste à suivre le mécanisme init.d consistant à leur donner des noms numériques:

def test_00_createEmptyObject(self):
    obj = MyObject()
    self.assertIsEqual(obj.property1, 0)
    self.assertIsEqual(obj.dict1, {})

def test_01_createObject(self):
    obj = MyObject(property1="hello", dict1={"pizza":"pepperoni"})
    self.assertIsEqual(obj.property1, "hello")
    self.assertIsDictEqual(obj.dict1, {"pizza":"pepperoni"})

def test_10_reverseProperty(self):
    obj = MyObject(property1="world")
    obj.reverseProperty1()
    self.assertIsEqual(obj.property1, "dlrow")

Cependant, dans de tels cas, vous pouvez envisager de structurer vos tests différemment afin de pouvoir vous baser sur des cas de construction antérieurs. Par exemple, dans ce qui précède, il pourrait être judicieux d’avoir une fonction "construct and veirfy" qui construit l’objet et valide son affectation de paramètres.

def make_myobject(self, property1, dict1):  # Must be specified by caller
    obj = MyObject(property1=property1, dict1=dict1)
    if property1:
        self.assertEqual(obj.property1, property1)
    else:
        self.assertEqual(obj.property1, 0)
    if dict1:
        self.assertDictEqual(obj.dict1, dict1)
    else:
        self.assertEqual(obj.dict1, {})
    return obj

def test_00_createEmptyObject(self):
    obj = self.make_object(None, None)

def test_01_createObject(self):
    obj = self.make_object("hello", {"pizza":"pepperoni"})

def test_10_reverseProperty(self):
    obj = self.make_object("world", None)
    obj.reverseProperty()
    self.assertEqual(obj.property1, "dlrow")
3
kfsone

Je suis d’accord avec l’affirmation selon laquelle une réponse «ne faites pas ça» est une mauvaise réponse. 

Dans une situation similaire, je n'ai qu'une seule source de données et un test efface le jeu de données, ce qui provoque l'échec des autres tests.

Ma solution consistait à utiliser les variables d'environnement du système d'exploitation de mon serveur Bamboo ...

(1) Le test de la fonctionnalité "purge des données" commence par une boucle while vérifiant l'état d'une variable d'environnement "BLOCK_DATA_PURGE". Si la variable "BLOCK_DATA_PURGE" est supérieure à zéro, la boucle écrit une entrée de journal indiquant qu'elle est en veille pendant une seconde. Une fois que "BLOCK_DATA_PURGE" a une valeur zéro, l'exécution continue pour tester la fonctionnalité de purge.

(2) Tout test unitaire nécessitant les données de la table incrémente simplement "BLOCK_DATA_PURGE" au début (dans setup ()) et décrémente la même variable dans pulldown (). 

Cela a pour effet de permettre à divers utilisateurs de données de bloquer la fonctionnalité de purge aussi longtemps qu'ils en ont besoin, sans craindre que la purge puisse s'exécuter entre les tests. Effectivement, l'opération de purge est poussée à la dernière étape ... ou au moins à la dernière étape qui requiert le jeu de données d'origine.

Aujourd'hui, je vais étendre cela pour ajouter plus de fonctionnalités afin de permettre à certains tests de REQUIRE_DATA_PURGE. Celles-ci inverseront effectivement le processus ci-dessus pour garantir que ces tests ne sont exécutés qu'après la purge des données pour tester la restauration des données.

2
kingsisyphus

J'ai implémenté un plugin nosedep pour Nose qui ajoute un support pour les dépendances de test et la priorisation de test.

Comme mentionné dans les autres réponses/commentaires, c’est souvent une mauvaise idée. Cependant, il peut exister des exceptions où vous voudriez le faire (dans mon cas, c’était la performance pour les tests d’intégration - avec un temps système énorme pour entrer dans un état testable, minutes vs heures).

Un exemple minimal est:

def test_a:
  pass

@depends(before=test_a)
def test_b:
  pass

Pour vous assurer que test_b est toujours exécuté avant test_a.

1
Zitrax

Voir l'exemple de WidgetTestCase sur http://docs.python.org/library/unittest.html#organizing-test-code , il est indiqué que

Les instances de classe exécuteront désormais chacune des méthodes test _ * (), avec self.widget créé et détruit séparément pour chaque instance.

Il peut donc être inutile de spécifier l'ordre des cas de test si vous n'accédez pas à des variables globales.

1
jiakai

La philosophie des tests unitaires est de les rendre indépendants les uns des autres. Cela signifie que la première étape de chaque test consistera toujours à repenser la façon dont vous testez chaque élément afin de respecter cette philosophie. Cela peut impliquer de changer votre approche des tests et votre créativité en limitant vos tests à de plus petites portées.

Cependant, si vous pensez toujours que vous avez besoin de tests dans un ordre spécifique (viable), vous pouvez essayer de vérifier la réponse à Python unittest.TestCase ordre d'exécution .

0
carrvo

Pour rendre aléatoire l'ordre des méthodes de test, vous pouvez corriger l'attribut unittest.TestLoader.sortTestMethodsUsing

if __== '__main__':
    import random
    unittest.TestLoader.sortTestMethodsUsing = lambda self, a, b: random.choice([1, 0, -1])
    unittest.main()

La même approche peut être utilisée pour appliquer l'ordre que vous avez besoin.

0
Tomasz Swider