2017-10-16 12 views
1

Mon framework d'automatisation utilise le type de test pytest setup/teardown à la place des appareils. J'ai aussi plusieurs niveaux de classes:pytest: obtention des valeurs de paramétrage lors de la configuration/démontage

BaseClass - le plus élevé, tous les tests inhriet de lui

FeatureClass - moyen, tous les tests liés aux programmes comportent en héritent

TestClass - tenir les tests réels

modifier, pour l'amour des exemples, je change la DB appelle à une simple impression

Je veux ajouter un rapport DB dans toutes les configurations/démontages. c'est-à-dire que je veux que le général BaseClasssetup_method crée une entrée DB pour le test et que teardown_method modifie l'entrée avec les résultats. J'ai essayé mais je n'arrive pas à sortir de la méthode les valeurs du test en cours pendant l'exécution. est-ce possible même? et sinon, comment pourrais-je faire autrement?

échantillons: (en base.py)

class Base(object): 

    test_number = 0 
    def setup_method(self, method): 
     Base.test_number += 1 
     self.logger.info(color.Blue("STARTING TEST")) 
     self.logger.info(color.Blue("Current Test: {}".format(method.__name__))) 
     self.logger.info(color.Blue("Test Number: {}".format(self.test_number))) 
     # --->here i'd like to do something with the actual test parameters<--- 
     self.logger.info("print parameters here") 

    def teardown_method(self, method): 
     self.logger.info(color.Blue("Current Test: {}".format(method.__name__))) 
     self.logger.info(color.Blue("Test Number: {}".format(self.test_number))) 
     self.logger.info(color.Blue("END OF TEST")) 

(en my_feature.py)

class MyFeature(base.Base): 

    def setup_method(self, method): 
     # enable this feature in program 
     return True 

(en test_my_feature.py)

class TestClass(my_feature.MyFeature): 

    @pytest.mark.parametrize("fragment_length", [1,5,10])  
    def test_my_first_test(self): 
     # do stuff that is changed based on fragment_length 
     assert verify_stuff(fragment_length) 

alors comment peut-i obtenir les paramètres dans setup_method, de la classe parente de base du framework de test?

+0

Utilisez-vous 'unittest.TestCase'? Ensuite, vous pouvez définir les valeurs dans 'setup' à' self' et les récupérer dans les tests. Plus de détails comme un exemple de code est nécessaire pour répondre à cette question. – Arunmozhi

+0

@Arunmozhi J'ai ajouté du code de base, j'espère que cela clarifiera ce que j'essaie de faire –

Répondre

2

La réponse brève: NON, vous ne pouvez pas faire cela. Et OUI, vous pouvez contourner cela.


Un peu plus: ces configurations unittest style & démontages sont faites uniquement pour la compatibilité avec les tests de style unittest. Ils ne supportent pas le montage du pytest, ce qui rend le pytest sympa. Par conséquent, ni le plugin unittest de pytest ni celui de pytest ne fournissent le contexte de ces méthodes de configuration/démontage. Si vous avez un request, function ou d'autres objets contextuels, vous pouvez obtenir dynamiquement les valeurs du projecteur via request.getfuncargvalue('my_fixture_name'). Cependant, tout ce que vous avez est self/cls, et method comme objet de la méthode de test elle-même (c'est-à-dire pas le nœud de pytest).

Si vous regardez à l'intérieur du plugin _pytest/unittest.py, vous trouverez ce code:

class TestCaseFunction(Function): 
    _excinfo = None 

    def setup(self): 
     self._testcase = self.parent.obj(self.name) 
     self._fix_unittest_skip_decorator() 
     self._obj = getattr(self._testcase, self.name) 
     if hasattr(self._testcase, 'setup_method'): 
      self._testcase.setup_method(self._obj) 
     if hasattr(self, "_request"): 
      self._request._fillfixtures() 

D'abord, notez que le setup_method() est appelé complètement isolé de l'objet du pytest (par exemple self que le noeud de test). Deuxièmement, notez que les appareils sont préparés après l'appel de setup_method(). Donc, même si vous pouviez y accéder, ils ne seraient pas prêts. Donc, en général, vous ne pouvez pas le faire sans une certaine supercherie.


Pour la supercherie, vous devez définir un crochet pytest/hookwrapper une fois, et rappelez-vous le noeud pytest en cours d'exécution:

conftest.py ou tout autre plug-in:

import pytest 

@pytest.hookimpl(hookwrapper=True) 
def pytest_runtest_protocol(item, nextitem): 
    item.cls._item = item 
    yield 

test_me.py:

import pytest 


class Base(object): 
    def setup_method(self, method): 
     length = self._item.callspec.getparam('fragment_length') 
     print(length) 


class MyFeature(Base): 
    def setup_method(self, method): 
     super().setup_method(method) 


class TestClass(MyFeature): 
    @pytest.mark.parametrize("fragment_length", [1,5,10])  
    def test_my_first_test(self, fragment_length): 
     # do stuff that is changed based on fragment_length 
     assert True # verify_stuff(fragment_length) 

Notez également que MyFeature.setup_method() mu st appelez le super(...).setup_method() du parent pour des raisons évidentes.

Le cls._item sera défini sur chaque appel (c'est-à-dire chaque appel de fonction avec chaque paramètre). Vous pouvez également placer l'élément ou les paramètres spécifiques dans un autre état global, si vous le souhaitez.

Veillez également à ne pas enregistrer le champ dans le item.instance. L'instance de la classe sera créée plus tard, et vous devez utiliser la méthode setup_instance/teardown_instance pour cela. Sinon, le champ de l'instance enregistrée n'est pas conservé et n'est pas disponible sous self._item dans setup_method().

Voici l'exécution:

============ test session starts ============ 
...... 
collected 3 items                                         

test_me.py::TestClass::test_my_first_test[1] 1 
PASSED 
test_me.py::TestClass::test_my_first_test[5] 5 
PASSED 
test_me.py::TestClass::test_my_first_test[10] 10 
PASSED 

============ 3 passed in 0.04 seconds ============ 
+0

merci pour la réponse détaillée, cela m'a vraiment aidé à mieux comprendre pytest. Je vais essayer de mettre en œuvre cela. Je vous vois explicitement utilisé 'fragment_length' pour obtenir sa valeur, donc si je veux obtenir tous les paramètres d'un certain test, ai-je besoin d'obtenir self._item.callspec.params et de parcourir ses valeurs? –

+1

@AvishayCohen Je ne peux pas dire avec certitude, mais très probablement oui. Tous les champs et méthodes ici font partie de l'API avancé de pytest. Le seul problème était que item/node/callspec ne sont pas exposés aux classes/méthodes d'unittest. Je pense que cela était intentionnel pour la rétrocompatibilité avec unittest, et n'est pas supposé pour l'utilisation normale de pytest. –

+0

ouais. ça marche super! –