web-dev-qa-db-fra.com

Passer un paramètre à une fonction fixture

J'utilise py.test pour tester du code DLL) enveloppé dans un python classe MyTester. Pour que je puisse valider, je dois enregistrer certaines données de test au cours des tests et faire plus de traitements par la suite.

Comme l'objet testeur est celui qui a obtenu les références aux variables et fonctions de la DLL, il est nécessaire de transmettre une liste des variables de la DLL à l'objet testeur pour chacun des fichiers de test (les variables à consigner sont les mêmes pour un test_ .. . fichier). Le contenu de la liste doit être utilisé pour enregistrer les données spécifiées.

Mon idée est de le faire en quelque sorte comme ceci:

import pytest

class MyTester():
    def __init__(self, arg = ["var0", "var1"]):
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

# located in conftest.py (because other test will reuse it)

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester()
    return _tester

# located in test_...py

# @pytest.mark.usefixtures("tester") 
class TestIt():

    # def __init__(self):
    #     self.args_for_tester = ["var1", "var2"]
    #     # how to pass this list to the tester fixture?

    def test_tc1(self, tester):
       tester.dothis()
       assert 0 # for demo purpose

    def test_tc2(self, tester):
       tester.dothat()
       assert 0 # for demo purpose

Est-il possible d'y parvenir de cette manière ou existe-t-il même un moyen plus élégant?

Habituellement, je pouvais le faire pour chaque méthode de test avec une sorte de fonction de configuration (style xUnit). Mais je veux obtenir une sorte de réutilisation. Est-ce que quelqu'un sait si c'est possible avec des appareils?

Je sais que je peux faire quelque chose comme ceci: (de la documentation)

@pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"])

Mais j'ai besoin de la paramétrisation directement dans le module de test. Est-il possible d'accéder à l'attribut params de la fixture à partir du module de test?

87
maggie

J'ai eu un problème similaire - j'ai un appareil appelé test_package, et plus tard, je voulais pouvoir passer un argument optionnel à ce projecteur lors de son exécution dans des tests spécifiques. Par exemple:

@pytest.fixture()
def test_package(request, version='1.0'):
    ...
    request.addfinalizer(fin)
    ...
    return package

(Peu importe ce que fait le fixture ou le type d'objet retourné package).

Il serait alors souhaitable d'utiliser d'une manière ou d'une autre ce projecteur dans une fonction de test de telle sorte que je puisse également spécifier l'argument version de ce projecteur à utiliser avec ce test. Ce n'est actuellement pas possible, mais pourrait faire une fonctionnalité de Nice.

En attendant, il était assez facile de faire en sorte que mon appareil retourne simplement une fonction qui effectue tout le travail effectué auparavant par l'appareil, mais me permet de spécifier le version argument:

@pytest.fixture()
def test_package(request):
    def make_test_package(version='1.0'):
        ...
        request.addfinalizer(fin)
        ...
        return test_package

    return make_test_package

Maintenant, je peux utiliser ceci dans ma fonction de test comme:

def test_install_package(test_package):
    package = test_package(version='1.1')
    ...
    assert ...

etc.

La solution tentée par le PO allait dans la bonne direction et, comme le suggère @ hpk42 réponse , le MyTester.__init__ pourrait simplement stocker une référence à la demande telle que:

class MyTester(object):
    def __init__(self, request, arg=["var0", "var1"]):
        self.request = request
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

Ensuite, utilisez ceci pour implémenter le fixture comme ceci:

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester(request)
    return _tester

Si vous le souhaitez, la classe MyTester peut être restructurée un peu pour que sa classe .args attribut peut être mis à jour après sa création pour modifier le comportement de tests individuels.

81
Iguananaut

Ceci est supporté nativement dans pytest via paramétrage indirect .

Dans votre cas, vous auriez:

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [['var1', 'var2']], indirect=True)
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
122
imiric

Vous pouvez accéder au module/à la classe/à la fonction à partir des fonctions de fixture (et donc de votre classe de testeur), voir interaction avec le contexte de test demandé à partir d'une fonction de fixture . Ainsi, vous pouvez déclarer certains paramètres sur une classe ou un module et le testeur peut le détecter.

11
hpk42

Pour améliorer un peu réponse d'imiric : un autre moyen élégant de résoudre ce problème consiste à créer des "paramètres paramétrables". Personnellement, je le préfère à la fonctionnalité indirect de pytest. Cette fonctionnalité est disponible à partir de pytest_cases , et l'idée originale a été suggérée par Sup3rGeo .

import pytest
from pytest_cases import param_fixture

# create a single parameter fixture
var = param_fixture("var", [['var1', 'var2']], ids=str)

@pytest.fixture
def tester(var):
    """Create tester object"""
    return MyTester(var)

class TestIt:
    def test_tc1(self, tester):
       tester.dothis()
       assert 1

Notez que pytest-cases fournit également @pytest_fixture_plus qui vous permet d’utiliser des repères de paramétrage sur vos appareils, et @cases_data qui vous permet de rechercher vos paramètres à partir de fonctions dans un module séparé. Voir doc pour plus de détails. Je suis l'auteur au fait;)

2
smarie