Services
Blog
English
pytest est un excellent framework de test Python qui repart d’une page blanche au lieu de suivre le patron xUnit.
Au lieu de :
class YourTestSuite(unittest.TestCase):
def setUp(self):
self.some_stuff = YourThing()
def test_delete(self):
self.some_stuff.delete()
self.assertFalse(self.some_stuff.exists())
On fait :
@pytest.fixture
def some_stuff():
return YourThing()
def test_delete(some_stuff):
some_stuff.delete()
assert not some_stuff.exists()
Quelques avantages méritent d’être notés :
Cependant, que faire avec du code complexe où l’on teste la colle entre plus d’une douzaine de fixtures ?
def test_delete(stuff, client, timestamp, bottle_contract, redeem_contract, blockchain, account):
# ...
On se retrouve avec beaucoup de fixtures dans la signature. D’où l’invention du patron superfixture. C’est simplement une classe de fixture avec des propriétés mises en cache, qui seront créées pour la durée du test au moment où elles sont nécessaires. C’est en gros du pur Python, qui exploite l’excellent décorateur functools.cached_property :
class Fixture:
@functools.cached_property
def stuff(self):
return Stuff()
@functools.cached_property
def blockchain(self):
return Blockchain.objects.create(name='ethlocal')
@functools.cached_property
def account(self):
return self.blockchain.account_set.create()
@functools.cached_property
def client(self):
client = APIClient()
client.login(self.account)
return client
@functools.cached_property
def bottle_contract(self):
return BottleContract.objects.create(
owner=self.account,
blockchain=self.blockchain,
)
@functools.cached_property
def redeem_contract(self):
return RedeemContract.objects.create(
bottle_contract=self.bottle_contract,
owner=self.account,
blockchain=self.blockchain,
)
Ensuite, vous pouvez l’utiliser ainsi dans vos tests :
@pytest.fixture
def fixture():
return Fixture()
def test_something(fixture):
response = fixture.client.post(
'/redeem',
dict(contract=fixture.redeem_contract),
)
fixture.redeem_contract.refresh_from_db()
assert fixture.redeem_contract.redeemed
Vous pouvez même transmettre des fixtures venant de plugins, par exemple :
@pytest.fixture
def fixture(mocker):
return Fixture(mocker)
Mais si vous voulez simplement qu’un objet soit créé, il faut appeler la propriété. Dans ce cas, ajoutez peut-être un commentaire pour que cela ne ressemble pas à une erreur :
def test_something(fixture):
fixture.redeem_contract # ensure redeem contract creation prior to test
# stuff
Voilà ! Travaillez intelligemment, pas durement ;)
