Formation gratuite Python
- Pourquoi Markdown – Tests Python
- # 152 Comprendre et utiliser l'AST de Python
- Transcription de l'épisode 2: Pytest vs Unittest vs Nose
- Enseigner Python et trouver des ressources pour les étudiants – Le véritable podcast Python
- Créer un jeu de plateforme avec Arcade et couvrir les actualités Python mensuellement – Le vrai podcast Python
Je vais aborder la syntaxe de prise en charge de pytest pour les fixtures de style xUnit.
Ensuite, je donnerai un exemple plus raisonnable et typique, en utilisant un seul ensemble de fonctions d’appareil.
Et abordez ensuite la question de la mixité des tests dans un fichier. Certains qui ont besoin de la ressource, d'autres pas.
En fonction de l’étendue que vous souhaitez pour vos appareils, vous définissez des paires configuration / démontage.
- Module (setup_module / teardown_module)
- Met les choses en place une fois pour le module, démonte après tout.
- Fonction (setup_function / teardown_function)
- Enveloppe chaque fonction de test avec des appels.
- Se fait appeler plusieurs fois, une fois pour chaque fonction
- Classe (setup_class / teardown_class)
- Comme au niveau du module, mais pour les classes, une fois pour une classe.
- Méthode (setup_method / teardown_method)
- Comme le niveau de fonction, mais pour les classes.
- Se fait appeler plusieurs fois, une fois pour chaque méthode de test dans une classe
Exemple utilisant des fixtures de module, fonction, classe et méthode
depuis __future__ import print_function
def setup_module (module):
print (' nsetup_module ()')
def teardown_module (module):
print ('teardown_module ()')
def setup_function (fonction):
print (' nsetup_function ()')
def teardown_function (fonction):
print (' nteardown_function ()')
def test_1 ():
print ('- test_1 ()')
def test_2 ():
print ('- test_2 ()')
classe TestClass:
@classmethod
def setup_class (cls):
print (' nsetup_class ()')
@classmethod
def teardown_class (cls):
print ('teardown_class ()')
def setup_method (auto, méthode):
print (' nsetup_method ()')
def teardown_method (auto, méthode):
print (' nteardown_method ()')
def test_3 (auto):
print ('- test_3 ()')
def test_4 (auto):
print ('- test_4 ()')
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
de __futur__ importation fonction_impression def setup_module(module): impression(' nsetup_module ()') def teardown_module(module): impression('teardown_module ()') def setup_function(une fonction): impression(' nsetup_function ()') def fonction_downdown(une fonction): impression(' nteardown_function ()') def test_1(): impression('- test_1 ()') def test_2(): impression('- test_2 ()') classe TestClass: @méthode de classe def setup_class(cls): impression (' nsetup_class ()') @méthode de classe def teardown_class(cls): impression ('teardown_class ()') def setup_method(soi, méthode): impression (' nsetup_method ()') def teardown_method(soi, méthode): impression (' nteardown_method ()') def test_3(soi): impression('- test_3 ()') def test_4(soi): impression('- test_4 ()') |
Et laisse courir pour voir le flux. J'ai supprimé certaines des lignes vierges supplémentaires pour ce poste.
$ py.test -s -v test_xunit_style_fixtures.py
================================= La session de test commence ============== ==================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collectionné 4 articles
test_xunit_style_fixtures.py:17: test_1
setup_module ()
setup_function ()
– test_1 ()
PASSÉ
teardown_function ()
test_xunit_style_fixtures.py:20: test_2
setup_function ()
– test_2 ()
PASSÉ
teardown_function ()
test_xunit_style_fixtures.py:40: TestClass.test_3
setup_class ()
setup_method ()
– test_3 ()
PASSÉ
teardown_method ()
test_xunit_style_fixtures.py:43: TestClass.test_4
setup_method ()
– test_4 ()
PASSÉ
teardown_method ()
teardown_class ()
teardown_module ()
============================= 4 passés en 0.01 secondes =============== ==============
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
$ py.tester –s –v test_xunit_style_fixtures.py ================================ tester session départs ================================ Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 4 articles test_xunit_style_fixtures.py:17: test_1 setup_module() setup_function() – test_1() PASSÉ fonction_downdown() test_xunit_style_fixtures.py:20: test_2 setup_function() – test_2() PASSÉ fonction_downdown() test_xunit_style_fixtures.py:40: TestClass.test_3 setup_class() setup_method() – test_3() PASSÉ teardown_method() test_xunit_style_fixtures.py:43: TestClass.test_4 setup_method() – test_4() PASSÉ teardown_method() teardown_class() teardown_module() ============================= 4 passé dans 0,01 secondes ============================== |
Exemple réaliste
En règle générale, vous ne combinez pas tous les types de projecteurs.
La plupart du temps, un seul style suffit, selon ce que vous configurez, initialisez, etc., et s'il doit être réinitialisé avant chaque test et nettoyé après chaque test.
Alors, rendons les choses un peu plus simples.
J'ai une ressource appelée resource_a. Je sais, nom ennuyeux. C’est juste quelque chose qui nécessite une configuration et une fonction de démontage.
Cela pourrait vraiment être n'importe quelle ressource:
- fichier temporaire
- répertoire temporaire
- connexion à la base de données
- transaction db qui doit être annulée après les tests
- connexion prise ouverte
- un générateur de signal émettant un signal de test
- vous obtenez la dérive
Dans cet exemple, j’ai utilisé des fixtures de niveau méthode afin que la configuration / le démontage ait lieu au début et à la fin du module, une fois pour tous les tests. C’est peut-être une opération coûteuse ou quelque chose du genre.
Voici notre exemple plus simple avec une ressource.
depuis __future__ import print_function
def resource_a_setup ():
print ('resources_a_setup ()')
def resource_a_teardown ():
print ('resources_a_teardown ()')
def setup_module (module):
print (' nsetup_module ()')
resource_a_setup ()
def teardown_module (module):
print (' nteardown_module ()')
resource_a_teardown ()
def test_1_that_needs_resource_a ():
print ('test_1_that_needs_resource_a ()')
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 |
de __futur__ importation fonction_impression def resource_a_setup(): impression('resources_a_setup ()') def resource_a_teardown(): impression('resources_a_teardown ()') def setup_module(module): impression(' nsetup_module ()') resource_a_setup() def teardown_module(module): impression(' nteardown_module ()') resource_a_teardown() def test_1_that_needs_resource_a(): impression('test_1_that_needs_resource_a ()') |
Et la sortie.
$ py.test -s -v test_realistic_one_func.py
================================= La session de test commence ============== ==================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collecté 1 objets
test_realistic_one_func.py:17: test_1_that_needs_resource_a
setup_module ()
resources_a_setup ()
test_1_that_needs_resource_a ()
PASSÉ
teardown_module ()
resources_a_teardown ()
============================ 1 a passé en 0.01 secondes =============== ==============
$ py.tester –s –v test_realistic_one_func.py ================================ tester session départs ================================ Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 1 articles test_realistic_one_func.py:17: test_1_that_needs_resource_a setup_module() resources_a_setup() test_1_that_needs_resource_a() PASSÉ teardown_module() resources_a_teardown() ============================= 1 passé dans 0,01 secondes ============================== |
Ajout d'une autre fonction de test
Ensuite, nous ajoutons un test qui n’a pas besoin de la ressource:
# …
def test_2_that_does_not ():
print (' ntest_2_that_does_not ()')
# … def test_2_that_does_not(): impression(' ntest_2_that_does_not ()') |
Et nous le relançons.
$ py.test -s -v test_realistic_two_funcs.py
================================= La session de test commence ============== ==================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collectionné 2 articles
test_realistic_two_funcs.py:17: test_1_that_needs_resource_a
setup_module ()
resources_a_setup ()
test_1_that_needs_resource_a ()
PASSÉ
test_realistic_two_funcs.py:20: test_2_that_does_not
test_2_that_does_not ()
PASSÉ
teardown_module ()
resources_a_teardown ()
============================= 2 passés en 0.01 secondes =============== ==============
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 |
$ py.tester –s –v test_realistic_two_funcs.py ================================ tester session départs ================================ Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 2 articles test_realistic_two_funcs.py:17: test_1_that_needs_resource_a setup_module() resources_a_setup() test_1_that_needs_resource_a() PASSÉ test_realistic_two_funcs.py:20: test_2_that_does_not test_2_that_does_not() PASSÉ teardown_module() resources_a_teardown() ============================= 2 passé dans 0,01 secondes ============================== |
Ce n’est pas vraiment un problème jusqu’à présent.
Comme le premier test a besoin de la ressource, la manière dont nous procédons est satisfaisante.
Problème: la ressource est configurée même lorsque nous n’en avons pas besoin.
Si nous voulons seulement exécuter une fonction, la deuxième, qui n’a pas besoin de la ressource, le projecteur est exécuté de toute façon.
$ py.test -s -v test_realistic_two_funcs.py::test_2_that_does_not
================================= La session de test commence ============== ==================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collectionné 3 articles
test_realistic_two_funcs.py:20: test_2_that_does_not
setup_module ()
resources_a_setup ()
test_2_that_does_not ()
PASSÉ
teardown_module ()
resources_a_teardown ()
============================ 1 a passé en 0.01 secondes =============== ==============
$ py.tester –s –v test_realistic_two_funcs.py::test_2_that_does_not ================================ tester session départs ================================ Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 3 articles test_realistic_two_funcs.py:20: test_2_that_does_not setup_module() resources_a_setup() test_2_that_does_not() PASSÉ teardown_module() resources_a_teardown() ============================= 1 passé dans 0,01 secondes ============================== |
C'est un gaspillage.
Si les montages sont assez longs, le test peut être sérieusement ralenti inutilement.
Création de classes pour séparer les besoins de fixtures
Vous pouvez résoudre ce problème de manière relativement propre en isolant les tests nécessitant une ressource dans leur propre module ou dans leur propre classe.
Je vais démontrer la solution de classe à ce problème.
Déplacez les fixtures du niveau du module au niveau de la classe et déplacez les tests qui utilisent la ressource dans la classe.
depuis __future__ import print_function
def resource_a_setup ():
print ('resources_a_setup ()')
def resource_a_teardown ():
print ('resources_a_teardown ()')
classe TestClass:
@classmethod
def setup_class (cls):
print (' nsetup_class ()')
resource_a_setup ()
@classmethod
def teardown_class (cls):
print (' nteardown_class ()')
resource_a_teardown ()
def test_1_that_needs_resource_a (auto):
print (' ntest_1_that_needs_resource_a ()')
def test_2_that_does_not ():
print (' ntest_2_that_does_not ()')
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
de __futur__ importation fonction_impression def resource_a_setup(): impression('resources_a_setup ()') def resource_a_teardown(): impression('resources_a_teardown ()') classe TestClass: @méthode de classe def setup_class(cls): impression (' nsetup_class ()') resource_a_setup() @méthode de classe def teardown_class(cls): impression (' nteardown_class ()') resource_a_teardown() def test_1_that_needs_resource_a(soi): impression(' ntest_1_that_needs_resource_a ()')
def test_2_that_does_not(): impression(' ntest_2_that_does_not ()') |
Maintenant, nous pouvons exécuter le test de manière isolée sans configuration / nettoyage coûteux et inutile des ressources.
$ py.test -s -v test_separation_of_fixtures.py::test_2_that_does_not
================================= La session de test commence ============== ==================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collectionné 2 articles
test_separation_of_fixtures.py:25: test_2_that_does_not
test_2_that_does_not ()
PASSÉ
============================ 1 a passé en 0.01 secondes =============== ==============
$ py.tester –s –v test_separation_of_fixtures.py::test_2_that_does_not ================================ tester session départs ================================ Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 2 articles test_separation_of_fixtures.py:25: test_2_that_does_not test_2_that_does_not() PASSÉ ============================= 1 passé dans 0,01 secondes ============================== |
Et la ressource est toujours traitée correctement lorsque nous en avons besoin.
$ py.test -s -v test_separation_of_fixtures.py
================================= La session de test commence ============== ==================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collectionné 2 articles
test_separation_of_fixtures.py:21: TestClass.test_1_that_needs_resource_a
setup_class ()
resources_a_setup ()
test_1_that_needs_resource_a ()
PASSÉ
teardown_class ()
resources_a_teardown ()
test_separation_of_fixtures.py:25: test_2_that_does_not
test_2_that_does_not ()
PASSÉ
============================= 2 passés en 0.01 secondes =============== ==============
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 |
$ py.tester –s –v test_separation_of_fixtures.py ================================ tester session départs ================================ Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 2 articles test_separation_of_fixtures.py:21: TestClass.test_1_that_needs_resource_a setup_class() resources_a_setup() test_1_that_needs_resource_a() PASSÉ teardown_class() resources_a_teardown() test_separation_of_fixtures.py:25: test_2_that_does_not test_2_that_does_not() PASSÉ ============================= 2 passé dans 0,01 secondes ============================== |
Dans bien des cas, la gestion des ressources de cette manière est tout à fait suffisante.
Cependant, j'estime que le mécanisme de montage pytest (que je traiterai dans mon prochain article) est une solution plus élégante et évolutive au problème.
[ad_2]