Deux façons d’implémenter des luminaires qui prennent des paramètres dans Pytest
La personnalisation des appareils avec des arguments est un cas d’utilisation courant. Cependant, je trouve cela peu documenté et rarement communiqué que cela est possible.
Je vais présenter deux façons de paramétrer les luminaires dans Pytest (usines de luminaires et paramétrisation implicite) – et terminer par une discussion sur le moment d’utiliser lequel.
Les appareils sont des fonctions d’assistance pour réduire les blocs de code répétitifs. Les meilleurs exemples sont un groupe de tests unitaires, où nous effectuons une initialisation plus ou moins complexe dans chaque test, comme la création d’une classe :
Pour cet article, une compréhension des tests unitaires en général, et pytest
en particulier est recommandé : une lecture possible est un de mes précédents messages, également présentation des montages et paramétrage – sur lequel nous nous appuyons ici.
class MySummationClass:
def sum(self, x, y):
return x + ydef test_sum():
my_summation_class = MySummationClass()
assert my_summation_class.sum(2, 3) == 5
def test_sum_neg():
my_summation_class = MySummationClass()
assert my_summation_class.sum(-2, -3) == -5
Au lieu de placer ce code partagé dans chaque test unitaire, nous pouvons l’encapsuler dans un appareil et l’incorporer dans chaque test :
@pytest.fixture
def my_summation_class():
return MySummationClass()def test_sum(my_summation_class):
my_summation_class = MySummationClass()
assert my_summation_class.sum(2, 3) == 5
def test_sum_neg(my_summation_class):
my_summation_class = MySummationClass()
assert my_summation_class.sum(-2, -3) == -5
Examinons l’exemple motivant suivant :
import math
from dataclasses import dataclass
from typing import Callable
import pytest@dataclass
class Circle:
radius: float
def get_area(self) -> float:
return 2 * math.pi * self.radius
@pytest.fixture
def new_circle() -> Circle:
return Circle(5)
def test_get_area_v1(new_circle: Circle) -> None:
assert new_circle.get_area() == pytest.approx(31.4159265359)
Nous définissons une classe de données définir un cercle : celui-ci est décrit via un rayon et propose une fonction pour calculer l’aire du cercle.
Nous définissons ensuite un appareil, en créant et en retournant un nouveau Circle
instance – et éventuellement tester la fonction get_area
en utilisant ce luminaire pour fournir un exemple d’objet.
Et maintenant, si nous voulions paramétrer cette fixation — c’est-à-dire avoir un moyen de définir une fixation qui prend le rayon comme argument et renvoie un cercle correspondant ? Cela serait utile et facilement faisable – même si peu communiqué à mon avis. Nous décrivons donc cela en détail ici.
Une fabrique de luminaires peut être utilisée chaque fois que nous voulons créer plusieurs objets n’importe où dans les fonctions.
Nous utilisons un appareil Pytest commun, mais au lieu de renvoyer l’objet facilement construit, nous renvoyons un objet fonction, en prenant radius
comme argument et créant un cercle approprié. Notez également la bonne annotation mypy:
@pytest.fixture
def new_circle_factory() -> Callable[[float], Circle]:
def _create(radius: float) -> Circle:
return Circle(radius)
return _createdef test_get_area_v2(new_circle_factory: Callable[[float], Circle]) -> None:
assert new_circle_factory(5).get_area() == pytest.approx(31.4159265359)
assert new_circle_factory(15).get_area() == pytest.approx(94.2477796077)
Une autre méthode découle du fait que les projecteurs prennent déjà implicitement en charge la paramétrisation. Cela peut être utilisé lorsque nous voulons combiner des projecteurs paramétrés avec d’autres types de paramétrage.
Pour cela, nous ajoutons quelques arguments à la fonction fixture — puis définissons ces arguments dans notre décorateur de paramétrage « normal » — mais ne les incluons pas dans la signature de la fonction de test. Par ici, pytest
transmettra implicitement cet argument à l’appareil :
@pytest.fixture
def new_circle_parametrized(radius: float) -> Circle:
return Circle(radius)@pytest.mark.parametrize(
"radius, expected_area", [(5, 31.4159265359), (15, 94.2477796077)]
)
def test_get_area_v3(new_circle_parametrized: Circle, expected_area: float) -> None:
assert new_circle_parametrized.get_area() == pytest.approx(expected_area)
Nous concluons cet article par une brève discussion sur le moment d’utiliser les deux méthodes : aucune n’est meilleure ou pire, les deux ont leurs cas d’utilisation et leurs scénarios d’application justifiés. Au lieu de cela, comme déjà taquiné dans leur introduction respective, cela dépend principalement de l’endroit et de la manière dont nous voulons utiliser les luminaires. Voici quelques façons de les utiliser :
- Lorsque nous voulons utiliser l’appareil de manière flexible pour définir des objets, potentiellement à la demande et à de nombreux endroits différents dans une ou plusieurs fonctions de test, nous avons besoin de la méthode 1.
- Si nous avons besoin de la portée « en dehors du programme normal », par exemple, à l’intérieur d’un autre décorateur paramétré, nous devons recourir à la méthode 2.
Ceci conclut cet excursus dans les fixtures paramétrables. J’espère que cela vous sera utile. Merci d’avoir lu!