web-dev-qa-db-fra.com

Comment configurer et désinstaller correctement ma classe pytest avec des tests?

J'utilise Selenium pour les tests de bout en bout et je ne sais pas comment utiliser setup_class et teardown_class méthodes.

J'ai besoin de configurer le navigateur dans setup_class méthode, puis effectuez une série de tests définis en tant que méthodes de classe et quittez finalement le navigateur dans teardown_class méthode.

Mais logiquement, cela semble une mauvaise solution, car mes tests ne fonctionneront pas avec la classe, mais avec l’objet. Je passe self param dans chaque méthode de test pour pouvoir accéder aux variables des objets:

class TestClass:

    def setup_class(cls):
        pass

    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass

    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass

    def teardown_class(cls):
        pass

Et il semble même ne pas être correct de créer une instance de navigateur pour class. Elle devrait être créée pour chaque objet séparément, non?

Donc, je dois utiliser __init__ et __del__ méthodes au lieu de setup_class et teardown_class?

61
avasin

Selon finalisation du montage/exécution du code de démontage , l'utilisation de addfinalizer est "historique".

En tant que note historique, un autre moyen d'écrire du code de démontage consiste à accepter un objet de requête dans votre fonction fixture et à appeler son request.addfinalizer une ou plusieurs fois:

La meilleure pratique actuelle pour l’installation et le démontage consiste à utiliser yield

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource(object):
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

En cours d'exécution, il en résulte

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===
58
Everett Toews

Lorsque vous écrivez "tests définis comme méthodes de classe", voulez-vous dire vraiment méthodes de classe (méthodes qui reçoivent sa classe en tant que premier paramètre) ou juste des méthodes régulières (méthodes qui reçoivent une instance en tant que premier paramètre)?

Étant donné que votre exemple utilise self pour les méthodes de test, je suppose que c'est la dernière, il vous suffit donc d'utiliser setup_method au lieu:

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

L'instance de la méthode de test est transmise à setup_method et teardown_method, mais peut être ignoré si votre code d'installation/de démontage n'a pas besoin de connaître le contexte de test. Plus d'informations peuvent être trouvées ici .

Je vous recommande également de vous familiariser avec fixtures de py.test, car ils constituent un concept plus puissant.

43
Bruno Oliveira

Comme @Bruno l'a suggéré, l'utilisation de luminaires pytest est une autre solution accessible pour les deux classes de test ou même pour de simples fonctions de test. Voici un exemple de test des fonctions python2.7 :

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

Donc, courir test_1... produit:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Remarquerez que stuff_i_setup est référencé dans la fixture, permettant à cet objet d’être setup et torn down pour le test avec lequel il interagit. Vous pouvez imaginer que cela pourrait être utile pour un objet persistant, tel qu'une base de données hypothétique ou une connexion, qui doit être effacé avant chaque test afin de les maintenir isolés.

23
ecoe

Cela pourrait aider http://docs.pytest.org/fr/latest/xunit_setup.html

Dans ma suite de tests, je regroupe mes cas de test en classes. Pour la configuration et le démontage dont j'ai besoin pour tous les tests élémentaires de cette classe, j'utilise les méthodes de classe setup_class(cls) et teardown_class(cls).

Et pour la configuration et le démontage dont j'ai besoin pour chaque cas de test, j'utilise les fonctions setup_method(method) et teardown_method(methods)

Exemple:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Maintenant, lorsque je lance mes tests, au début de l'exécution de TestClass, il enregistre les détails de son exécution, de sa fin d'exécution et de la même chose pour les méthodes.

Vous pouvez ajouter d'autres étapes de configuration et de démontage que vous pourriez avoir aux emplacements respectifs.

J'espère que ça aide!

16
Kiran Vemuri

Votre code devrait fonctionner exactement comme vous le souhaiteriez si vous ajoutez @classmethod décorateurs.

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

Voir http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/

10
Okken