Considérez le Pytest suivant:
import pytest
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture
def timeline():
return TimeLine()
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
if __name__ == "__main__":
pytest.main([__file__])
Le test test_timeline
utilise un appareil Pytest, timeline
, qui a lui-même l'attribut instances
. Cet attribut est itéré dans le test, de sorte que le test ne réussit que si l'assertion est vérifiée pour chaque instance
dans timeline.instances
.
Cependant, ce que j'aimerais réellement faire, c'est générer 3 tests, dont 2 devraient réussir et 1 échouer. J'ai essayé
@pytest.mark.parametrize("instance", timeline.instances)
def test_timeline(timeline):
assert instance % 2 == 0
mais cela conduit à
AttributeError: 'function' object has no attribute 'instances'
Si je comprends bien, dans les appareils Pytest, la fonction "devient" sa valeur de retour, mais cela ne semble pas encore être arrivé au moment où le test est paramétré. Comment puis-je configurer le test de la manière souhaitée?
De https://bitbucket.org/pytest-dev/pytest/issues/349/using-fixtures-in-pytestmarkparametrize , il semblerait qu'il ne soit actuellement pas possible d'utiliser des appareils dans pytest.mark.parametrize
.
Ceci est en fait possible via paramétrage indirect .
Cet exemple fait ce que vous voulez avec pytest 3.1.2:
import pytest
class TimeLine:
def __init__(self, instances):
self.instances = instances
@pytest.fixture
def timeline(request):
return TimeLine(request.param)
@pytest.mark.parametrize(
'timeline',
([1, 2, 3], [2, 4, 6], [6, 8, 10]),
indirect=True
)
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
if __name__ == "__main__":
pytest.main([__file__])
Voir aussi cette question similaire .
au lieu de la paramétrisation indirecte, ou de ma solution hacky ci-dessous impliquant l'héritage, vous pouvez également utiliser l'argument params
pour @pytest.fixture()
- je pense que c'est la solution la plus simple peut-être?
import pytest
class TimeLine:
def __init__(self, instances=[0, 0, 0]):
self.instances = instances
@pytest.fixture(params=[
[1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def timeline(request):
return TimeLine(request.param)
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures
L'utilisation de la paramétrisation indirecte fonctionne, mais je trouve la nécessité d'utiliser request.param
comme une variable magique sans nom un peu maladroite.
Voici un modèle que j'ai utilisé. C'est maladroit d'une manière différente, sans doute, mais peut-être le préférerez-vous aussi!
import pytest
class TimeLine:
def __init__(self, instances):
self.instances = instances
@pytest.fixture
def instances():
return [0, 0, 0]
@pytest.fixture
def timeline(instances):
return TimeLine(instances)
@pytest.mark.parametrize('instances', [
[1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
le luminaire timeline
dépend d'un autre luminaire appelé instances
, dont la valeur par défaut est [0,0,0]
, mais dans le test réel, nous utilisons parametrize
pour injecter une série de valeurs différentes pour instances
.
l'avantage que je vois, c'est que tout a un bon nom, et vous n'avez pas besoin de passer ce indirect=True
drapeau.
C'est un sujet déroutant, car les gens ont tendance à penser que les luminaires et les paramètres sont la même chose. Ils ne sont pas et ne sont même pas collectés au cours de la même phase du test Pytest. De par leur conception, les paramètres sont censés être collectés lorsque les tests sont collectés (la liste des nœuds de test est en cours de construction), tandis que les appareils sont destinés à être exécutés lorsque les nœuds de test sont exécutés (la liste des nœuds de test est fixe et ne peut pas être modifiée). Le contenu du luminaire ne peut donc pas être utilisé pour modifier le nombre de nœuds de test - c'est le rôle des paramètres. Voir aussi mon réponse détaillée ici .
C'est la raison pour laquelle votre question n'a pas de solution "en l'état": vous devez placer les instances Timeline
dans un paramètre, pas dans un fixture. Comme ça:
import pytest
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
def test_timeline(timeline):
assert timeline % 2 == 0
réponse de Kurt Peek mentionne un autre sujet qui à mon humble avis ajoute de la confusion ici car il n'est pas directement lié à votre question. Puisqu'il est mentionné, et même accepté comme la solution, laissez-moi élaborer: en effet dans pytest, vous ne pouvez pas utiliser un appareil dans un @pytest.mark.parametrize
. Mais même si vous le pouviez, cela ne changerait rien.
Étant donné que cette fonctionnalité est désormais disponible dans pytest-cases
en version bêta (je suis l'auteur), vous pouvez vous essayer. La seule façon de faire fonctionner votre exemple même avec cette fonctionnalité est toujours la même: pour supprimer la liste de l'appareil. Vous vous retrouvez donc avec:
import pytest
from pytest_cases import pytest_parametrize_plus, fixture_ref
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
@pytest_parametrize_plus("t", [fixture_ref(timeline)])
def test_timeline(t):
assert t % 2 == 0
Ce qui est le même que l'exemple précédent avec une couche supplémentaire, probablement inutile. Remarque: voir aussi cette discussion .
Votre parametrize paramset référence la fonction. Peut-être que vous voulez faire référence à Timeline.instances, pas à la fonction/timeline/du luminaire:
@pytest.mark.parametrize("instance", Timeline.instances)
def test_func(instance):
pass
Je pense que vous cherchez à ajouter une marque xfail à un ensemble de paramètres, peut-être avec une raison?
def make_my_tests():
return [0, 2, mark.xfail(1, reason='not even')]
@mark.parametrize('timeline', paramset=make_my_tests(), indirect=True)
def test_func(timeline):
assert timeline % 2 == 0