Par Holger Krekel, l'auteur entre autres de Tox, pypy, devpi
Un équivalent à Unittest, mâture, avec une excellent API et d'excellentes possibilité de personalisation.
Toujours activement développé, et très bien documenté.
Installation : pip install pytest
--duration=10
-x
(ou --maxfail=n
)--pdb
--pastebin=failed
--showlocals --tb=long
@pytest.mark.functional
py.test -k test_foo
par chemin : py.test tests/bar/
par module : py.test test_baz.py
par « mark » : py.test -m functional
ou
py.test -m "not functional"
rapports d'échecs intelligents :
Il y en a beaucoup d'autres. Beaucoup.def test_eq_dict(self):
> assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
E assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'b': 1} != {'b': 2}
E Left contains more items:
E {'c': 0}
E Right contains more items:
E {'d': 0}
E Use -v to get the full diff
Fixtures par injection de dépendance,
peuvent être utilisées à la place de (setUp|tearDown)(function|class|module|package)
abracadabradef test_view(rf, settings):
settings.LANGUAGE_CODE = 'fr'
request = rf.get('/')
response = home_view(request)
assert response.status_code == 302
Écrire une seule fois le test, le lancer avec plusieurs entrées
Travailler plusmoins pour tester plus@pytest.mark.parametrize(("input", "expected"),
[("3+5", 8), ("2+4", 6)])
def test_eval(input, expected):
assert eval(input) == expected
Au niveau global, dans le fichier pytest.ini : principalement pour la déclaration des « mark » et les options par défaut
--reuse-db c'est de la balle[pytest]
addopts = --reuse-db --tb=native
python_files=test*.py
markers =
es_tests: mark a test as an elasticsearch test.
Au niveau local, impacte le répertoire en cours et ses descendants : pour les fixtures, les plugins, les hooks
On parle pas du même impactimport pytest, os
def pytest_configure():
# Shortcut to using DJANGO_SETTINGS_MODULE=... py.test
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings_test'
# There's a lot of python path hackery done in our manage.py
import manage # noqa
Il existe de très nombreux plugins intégrés, mais aussi de la communauté
--create-db
, fixtures (rf, client, admin_client...)--cov myproj tests/
--lf
-n NUM
--reuse-db
--create-db
bon pour ma santé mentale[pytest]
addopts = --reuse-db
@pytest.mark.django_db
def test_avec_db(self): # fonction/méthode
@pytest.mark.django_db
class TestFoo(TestCase): # classe
pytestmark = pytest.mark.django_db # module
De toute manière, qui a besoin d'une DB de nos jours
dans conftest.py
Tant pis pour toidef pytest_collection_modifyitems(items):
for item in items:
item.keywords['django_db'] = pytest.mark.django_db
Utilisation de simples assert
Pas de camelCase comme self.assertEqual
assert 1 == 2
ou self.assertEqual(1, 2)
assert True
ou self.assertTrue(True)
assert not None
ou self.assertIsNotNone(None)
self.assertAbstractSingletonProxyFactoryBean
. Just joking. Or am I?
Pourquoi se passer de quelque chose de
versus + test
Naming is hard.Pas d'injection de dépendance (et donc pas non plus de paramètres), bien qu'on puisse utiliser les fixtures autouse
Pas de fixture bundling, donc les tests sont plus lents, mais Django 1.8 test case data setup et la possibilité de faire des builds en parallèle avec Travis
Des broutilles je vous disfind . -name test* | wc -l
ag "class Test" | wc -l
ag "def test_" | wc -l
ag "self.assert|ok_|eq_" | wc -l
Je l'ai déjà dis deux fois, py.test est compatible avec votre code actuel.
Nan mais en vrai pour l'ajout d'un fichier de fixtures de données.
Haha.py.test
versus
REUSE_DB=1 python manage.py test --noinput --logging-clear-handlers --with-id
Rajout de @pytest.mark.django_db
sur notre TestCase
de base
Rajouts de pytestmark = pytest.mark.django_db
dans les 35
fichiers qui en ont besoin
J'ai espoir de pouvoir utiliser cette featuredef pytest_collection_modifyitems(items):
for item in items:
item.keywords['django_db'] = pytest.mark.django_db
Il manquait des appels à super()
dans les
(setUp|tearDown)(class)
,
magouille sur le PYTHONPATH
dans manage.py :
J'ai vomi dans ma boucheimport manage.py
@pytest.fixture(autouse=True, scope='session')
def _load_testapp():
installed_apps = getattr(settings, 'INSTALLED_APPS')
setattr(settings, 'INSTALLED_APPS', installed_apps + extra_apps)
django.db.models.loading.cache.loaded = False
tox
permet d'avoir un PYTHONHASHSEED
différent à chaque run.
Mais du coup, il faut faire attention aux tests sur les dictionnaires
(non ordonnés !)
Pas de recompilation et récupération des statics pour les tests :
de toute façon je fais que du back-end@pytest.fixture(autouse=True)
def mock_inline_css(monkeypatch):
import amo.helpers
monkeypatch.setattr(amo.helpers, 'is_external',
lambda css: True)
Avec py.test, tox et travis, c'est un jeu d'enfant
De plus de 50 minutes à moins de 20
Je sais que c'est lent. Mais c'est BEAUCOUP MOINS lent.pip install pytest && py.test
formation à py.test : http://www.merlinux.eu/~hpk/testing.pdf
ces slides : http://mathieu.agopian.info/presentations/2015_05_djangocong/
plus d'excuses