Python pas cher
Dans les appareils de style pytest xUnit, j’ai présenté un problème où:
- Deux tests existent dans un fichier de test.
- On utilise une ressource.
- L’autre ne le fait pas.
- Les fixtures au niveau des modules ne fonctionnent pas si vous voulez juste exécuter la fonction qui n’utilise pas la ressource.
J'ai ensuite présenté les installations au niveau de la classe comme un moyen de résoudre le problème de la séparation.
Dans cet article, je vais utiliser des appareils pytest pour résoudre le même problème.
Je ne vais pas entrer dans les détails de tous les goodies que vous obtenez avec les appareils les plus authentiques.
Je vais juste rester à résoudre ce problème.
Dans les prochains articles, je donnerai plus de détails sur les appareils pytest.
le problème
Voici le code qui nous a posé problème la dernière fois:
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 ()')
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 |
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 ()') def test_2_that_does_not(): impression(' ntest_2_that_does_not ()') |
Le problème est que si je veux juste lancer ‘test_2_that_does_not ()’, les fonctions de fixture pour resource_a sont appelées, même si je n’ai pas besoin qu’elles soient appelées.
$ 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 =============== |
la solution de fixation pytest
Au lieu de déplacer les installations et les tests liés à resource_a dans une classe, nous:
- Pytest d'importation
- Utilisez le décorateur de luminaires pytest pour spécifier ‘resource_a_setup ()’ en tant que luminaire.
- Spécifiez le projecteur en tant que portée du module. Ainsi, si deux tests en ont besoin, l'installation / le démontage ne sera appelé qu'une fois.
- Indiquez "resource_a_teardown ()" comme finaliseur pour "resource_a_setup ()". Pour ce faire, nous devons ajouter un paramètre ‘request’ à la fonction de configuration. Notez également que la fonction de finalisation peut être très locale à la fonction de configuration, même définie dans celle-ci.
- Incluez "resource_a_setup" dans la liste des paramètres pour les tests utilisant resource_a.
Je vais également ajouter une fonction de test supplémentaire qui utilise la ressource pour prouver que la portée du module fonctionne.
Pour que les appareils pytest fonctionnent, les étapes n ° 1, n ° 2 et n ° 5 sont tout ce dont vous avez vraiment besoin.
L’étape 3 n’est nécessaire que si vous souhaitez modifier la valeur par défaut (‘function’).
L'étape 4 n'est nécessaire que si vous souhaitez inclure une fonction de démontage.
Donc, voici mon code.
depuis __future__ import print_function
pytest d'importation
@ pytest.fixture (scope = 'module')
def resource_a_setup (demande):
print (' nresources_a_setup ()')
def resource_a_teardown ():
print (' nresources_a_teardown ()')
request.addfinalizer (resource_a_teardown)
def test_1_that_needs_resource_a (resource_a_setup):
print ('test_1_that_needs_resource_a ()')
def test_2_that_does_not ():
print (' ntest_2_that_does_not ()')
def test_3_that_does (resource_a_setup):
print (' ntest_3_that_does ()')
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 |
de __futur__ importation fonction_impression importation pytest @pytest.fixation(portée='module') def resource_a_setup(demande): impression(' nresources_a_setup ()') def resource_a_teardown(): impression(' nresources_a_teardown ()') demande.addfinalizer(resource_a_teardown) def test_1_that_needs_resource_a(resource_a_setup): impression('test_1_that_needs_resource_a ()') def test_2_that_does_not(): impression(' ntest_2_that_does_not ()') def test_3_that_does(resource_a_setup): impression(' ntest_3_that_does ()') |
N'exécutant que 'test_2_that_does_not':
$ py.test -s -v test_three_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é 4 articles
test_three_funcs.py:14: test_2_that_does_not
test_2_that_does_not ()
PASSÉ
=============== 1 a passé en 0.01 secondes ===============
$ py.tester –s –v test_three_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é 4 articles test_three_funcs.py:14: test_2_that_does_not test_2_that_does_not() PASSÉ =============== 1 passé dans 0,01 secondes =============== |
Tout courir:
$ py.test -s -v test_three_funcs.py
================== La session de test commence ===================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collectionné 3 articles
test_three_funcs.py:11: test_1_that_needs_resource_a
resources_a_setup ()
test_1_that_needs_resource_a ()
PASSÉ
test_three_funcs.py:14: test_2_that_does_not
test_2_that_does_not ()
PASSÉ
test_three_funcs.py:17: test_3_that_does
test_3_that_does ()
PASSÉ
resources_a_teardown ()
=============== 3 passés en 0,01 seconde ===============
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 |
$ py.tester –s –v test_three_funcs.py ================= tester session départs ================== Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 3 articles test_three_funcs.py:11: test_1_that_needs_resource_a resources_a_setup() test_1_that_needs_resource_a() PASSÉ test_three_funcs.py:14: test_2_that_does_not test_2_that_does_not() PASSÉ test_three_funcs.py:17: test_3_that_does test_3_that_does() PASSÉ resources_a_teardown() =============== 3 passé dans 0,01 secondes =============== |
quelques avantages des luminaires pytest
Nous pouvons immédiatement constater des avantages intéressants.
- Il est évident que les tests utilisant une ressource sont listés dans la liste des paramètres de test.
- Je n'ai pas besoin de créer artificiellement des classes (ni de déplacer des tests d'un fichier à un autre) pour séparer l'utilisation des fixtures.
- Le code de démontage est étroitement associé au code d'installation d'une ressource.
- L'étendue de la durée de vie de la ressource est spécifiée à l'emplacement du code d'installation de la ressource. Cela finit par être un énorme avantage lorsque vous voulez manipuler la portée pour gagner du temps sur les tests. Si tout commence à se dégrader, c’est une modification d’une ligne permettant de spécifier l’étendue de la fonction et d’instaurer / de supprimer toutes les fonctions / méthodes.
- C’est moins de code. La solution pytest est plus petite que la solution de classe.
un exemple encore plus petit
J'ai indiqué précédemment dans la solution que les étapes 3 et 4 sont facultatives.
Jetons un coup d’œil au code simplifié si nous allons simplement avec les valeurs par défaut.
depuis __future__ import print_function
pytest d'importation
@ pytest.fixture ()
def resource_a ():
print (' nresources_a () "setup"')
def test_1_that_needs_resource_a (resource_a):
print ('test_1_that_needs_resource_a ()')
def test_2_that_does_not ():
print (' ntest_2_that_does_not ()')
def test_3_that_does (resource_a):
print ('test_3_that_does ()')
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 |
de __futur__ importation fonction_impression importation pytest @pytest.fixation() def ressource_a(): impression(' nresources_a () "setup"') def test_1_that_needs_resource_a(ressource_a): impression('test_1_that_needs_resource_a ()') def test_2_that_does_not(): impression(' ntest_2_that_does_not ()') def test_3_that_does(ressource_a): impression('test_3_that_does ()') |
La différence?
- Pas de code de démontage (finaliseur). Donc, pas besoin d'un paramètre de demande pour la fonction d'installation.
- Aucune portée spécifiée. La valeur par défaut appelle "resource_a" avant chaque fonction / méthode qui en a besoin.
- Oh oui. J'ai également raccourci le nom de la ressource. Déposer la ‘_setup’.
Cette version abrégée est plus typique de la façon dont je commencerais à écrire mon code de test.
J'ajoute seulement des finaliseurs (démontage) si nécessaire pour la ressource.
Il est intéressant de noter que seul le dispositif de ressources doit se soucier du finaliseur.
Vous pouvez l'ajouter si vous en avez besoin, et les modifications nécessaires ne concernent que le code de l'appareil d'installation.
Je suis aussi généralement d'accord avec le niveau de fonction au début.
Je suis attentif aux temps d'exécution et à la détermination réaliste des ressources nécessaires, et je modifie la portée si nécessaire.
Et encore une fois, ce violon est isolé du code de la ressource.
Les tests ne doivent pas être modifiés pour prendre en charge différentes portées.
Alors, voici mon essai:
$ py.test -s -v test_three_funcs_small.py
================== La session de test commence ===================
plateforme darwin – Python 2.7.5 – pytest-2.3.4 – / usr / bin / python
collectionné 3 articles
test_three_funcs_small.py:8: test_1_that_needs_resource_a
resources_a () "configuration"
test_1_that_needs_resource_a ()
PASSÉ
test_three_funcs_small.py:11: test_2_that_does_not
test_2_that_does_not ()
PASSÉ
test_three_funcs_small.py:14: test_3_that_does
resources_a () "configuration"
test_3_that_does ()
PASSÉ
=============== 3 passés en 0.03 secondes ===============
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 |
$ py.tester –s –v test_three_funcs_small.py ================= tester session départs ================== Plate-forme Darwin – Python 2.7.5 – pytest–2.3.4 – /usr/poubelle/python collecté 3 articles test_three_funcs_small.py:8: test_1_that_needs_resource_a ressources_a() "installer" test_1_that_needs_resource_a() PASSÉ test_three_funcs_small.py:11: test_2_that_does_not test_2_that_does_not() PASSÉ test_three_funcs_small.py:14: test_3_that_does ressources_a() "installer" test_3_that_does() PASSÉ =============== 3 passé dans 0,03 secondes =============== |
emballer
Mon objectif principal pour ce billet était de montrer que l’utilisation de fixtures pytest est au moins aussi simple que d'utiliser la solution de montage de classe pour séparer l'utilisation de montage.
J'espère l'avoir démontré.
Faites-le moi savoir si j'ai laissé quelques questions ouvertes.
Il y a beaucoup plus à couvrir, et j’ai un plan général pour aller de l’avant, mais j’aimerais beaucoup votre contribution.
[ad_2]