trouver un expert Python
Dans Delayed assert / plusieurs échecs par test, j’ai présenté une première tentative d’écriture d’une fonction ‘expect ()’ qui permettra
une fonction de test pour collecter plusieurs échecs et ne pas arrêter l'exécution jusqu'à la fin du test.
Il n’ya pas de gros problème avec cette méthode.
Je n’aime pas avoir à appeler «assert_expectations ()» dans le test.
Ce serait cool d'insérer cette partie dans un plugin.
Donc, même si ce n’est pas le code le plus joli, voici une première tentative pour en faire un plugin.
Code de test qui utilise expect ()
L’objectif principal de cette première itération est de supprimer l’appel ‘assert_expectations ()’ des tests.
J'aimerais que mes tests s'appellent simplement "expect ()", comme ceci:
test_delayed_assert.py
de delay_assert import s'attendre
def test_should_pass ():
s'attendre (1 == 1, 'on est un')
def test_should_fail ():
attendre (1 == 2)
s'attendre (3 == 4, 'trois est quatre')
de retardé_assert importation attendre def test_should_pass(): attendre(1 == 1, 'on est un') def test_should_fail(): attendre(1 == 2) attendre(3 == 4, "trois est quatre") |
Le plugin conftest.py local pour une assertion différée
Les appels "expect ()" vont générer une liste d’échecs.
Nous devons saisir cette liste et faire échouer le test.
Tout d’abord, il semble utile de s’assurer que la liste «_failed_expectations» est vide au début du test. J’ai donc ajouté un appel à l’effacer dans «pytest_runtest_setup ()».
Ensuite, dans "pytest_report_teststatus ()", je recherche les échecs et remplis les mots "report.outcome" et "report.longrepr".
conftest.py
importer delay_assert
def pytest_runtest_setup (item):
delay_assert.clear_expectations ()
def pytest_report_teststatus (rapport):
si report.when == "call":
sinon report.failed:
si delay_assert.any_failures ():
report.outcome = "a échoué"
report.longrepr = delay_assert.get_failure_report ()
delay_assert.clear_expectations ()
importation retardé_assert def pytest_runtest_setup(article): retardé_assert.clear_expectations() def pytest_report_teststatus(rapport): si rapport.quand == "appel": si ne pas rapport.échoué: si retardé_assert.any_failures(): rapport.résultat = "échoué" rapport.longrepr = retardé_assert.get_failure_report() retardé_assert.clear_expectations() |
Modifications apportées à delay_assert.py
Le code dans «conftest.py» appelle de nouvelles fonctions dans «delay_assert.py».
Voici les modifications apportées à ‘delay_assert.py 'pour que cela fonctionne.
delay_assert.py
# —- Appelé des tests
def expect (expr, msg = ''):
si non expr:
_log_failure (msg)
# —– Appelé depuis le plugin pytest
def clear_expectations ():
global _failed_expectations
_failed_expectations = []
def any_failures ():
return bool (_failed_expectations)
def get_failure_report ():
si any_failures ():
_failed_expectations.append ('Attentes infructueuses:% s'% len (_failed_expectations))
return (' n'.join (_failed_expectations))
autre:
revenir ''
# —— Garder _log_failure séparé, principalement parce que c'est du code laid
import inspecter
importer os.path
_failed_expectations = []
def _log_failure (msg = ''):
(nomfichier, ligne, nomfonction, liste contextuelle) = inspect.stack ()[2][1:5]
filename = os.path.basename (nom_fichier)
context = contextlist[0]
msg = '% s n'% msg si msg sinon ''
_failed_expectations.append ('>% s% s% s:% s n ——–'% (contexte, msg, nom du fichier, ligne))
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# —- Appelé des tests def attendre(expr, msg=''): si ne pas expr: _log_failure(msg) # —– Appelé depuis le plugin pytest def clear_expectations(): global _expectations_échouées _expectations_échouées = [[[[] def any_failures(): revenir bool(_expectations_échouées) def get_failure_report(): si any_failures(): _expectations_échouées.ajouter('Echec des attentes:% s' % len(_expectations_échouées)) revenir (' n'.joindre(_expectations_échouées)) autre: revenir '' # —— Garder _log_failure séparé, principalement parce que c'est du code laid importation inspecter importation os.chemin _expectations_échouées = [[[[] def _log_failure(msg=''): (nom de fichier, ligne, funcname, contextlist) = inspecter.empiler()[[[[2][[[[1:5] nom de fichier = os.chemin.nom de base(nom de fichier) le contexte = contextlist[[[[0] msg = '% s n' % msg si msg autre '' _expectations_échouées.ajouter('>% s% s% s:% s n ——–' % (le contexte, msg, nom de fichier,ligne)) |
En le voyant en action
Juste pour vous montrer que cela fonctionne réellement.
essai
$ python -m pytest -s test_delayed_assert.py
===================================== La session de test commence ========== ===========================
plateforme darwin – Python 2.7.9 – py-1.4.26 – pytest-2.6.4
collectionné 2 articles
test_delayed_assert.py .F
======================================== ECHECS ======= ====================================
______________________________________ test_should_fail _______________________________________
> attendez (1 == 2)
test_delayed_assert.py:7
——–
> s'attendre (3 == 4, 'trois, c'est quatre')
trois est quatre
test_delayed_assert.py:8
——–
Attentes ratées: 2
============================= 1 a échoué, 1 s'est passé en 0,01 seconde ============= ==================
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 |
$ python –m pytest –s test_delayed_assert.py ===================================== tester session départs ===================================== Plate-forme Darwin – Python 2.7.9 – py–1.4.26 – pytest–2.6.4 collecté 2 articles test_delayed_assert.py .F ========================================== LES ÉCHECS =========================================== ______________________________________ test_should_fail _______________________________________ > attendre(1 == 2) test_delayed_assert.py:7 –––– > attendre(3 == 4, "trois est quatre") Trois est quatre test_delayed_assert.py:8 –––– Échoué Attentes:2 ============================= 1 échoué, 1 passé dans 0,01 secondes ============================== |
Problèmes possibles et choses que je n'aime pas
Cela semble fonctionner, du moins dans ce cas de test simple.
Je ne suis pas ravi que la fonctionnalité soit partagée entre deux fichiers, «conftest.py» et «delay_assert.py». Je ne sais pas si c’est juste que je sois pointilleux.
Voici ma liste initiale de problèmes possibles.
- La liste '_failed_expectations' peut ne pas fonctionner correctement pour la parallélisation, par exemple avec xdist.
- La fonction expect () serait plus cool si elle effectuait certaines assertions comme voodoo que pytest utilise pour rendre le rapport très lisible. Ce n'est pas obligatoire, mais ce serait cool.
- Je ne suis pas sûr d’être ravi du nom «différé», puisque la fonction est «expect ()», et qu’il n’y a plus d’affirmation. Mais "expect ()" signifie quelque chose de différent pour certaines personnes, et il y a déjà une attente pytest dans le référentiel. Peut-être «multi-échec»? Nah. C'est nul.
- … Vraiment, il y avait plus de choses que je n’aimais pas à ce sujet. Je pense que la médecine froide et le porteur se mélangent et,… ils sont partis.
Je suis sûr que tout le monde qui lit ceci peut trouver des failles dans ma solution.
S'il vous plaît, faites-moi savoir.
Solutions alternatives
Quelques personnes ont indiqué qu’il serait préférable de se connecter à «pytest_runtest_makereport» plutôt qu’à «pytest_report_teststatus».
C’est peut-être tout à fait valable, mais je n’ai pas encore essayé d’y prendre part.
J’ai aussi pensé à faire de «expect» un vrai projecteur, ce qui permettrait certainement de tout mettre dans un fichier. Juste pas sûr si j'aime avoir les tests doivent déclarer un résultat attendu.
Cependant, cela pourrait ne pas être si mauvais.
Prochaines étapes
J'aimerais avoir des retours à ce sujet et des idées pour l’améliorer.
J'aimerais également continuer à montrer comment en faire un plug-in et un package pytest installables, etc.
Ensuite, peut-être le pousser vers github ou bitbucket, pour permettre les contributions des autres.
…
Quoi qu’il en soit, c’est amusant de mettre ça en public. Les verrues et tous.
[ad_2]