Dans un commentaire en faveur de la réponse acceptée , Joe demande:
Existe-t-il un moyen d'imprimer sur la console ET de capturer la sortie afin qu'elle s'affiche dans le rapport junit?
Sous UNIX, cela est communément appelé départ . Idéalement, le tee plutôt que la capture serait la valeur par défaut de py.test. Dans l'idéal, ni py.test ni aucun autre plugin py.test tiers existant (... que je sache, de toute façon ) ne prend en charge le tee - malgré le fait que Python supporte trivialement le tee out-of-the-box .
Le py.test de correction de singe pour faire tout ce qui n'est pas pris en charge n'est pas anodin. Pourquoi? Parce que:
- La plupart des fonctionnalités de py.test sont verrouillées derrière un
_pytest
package privé non destiné à être importé en externe. Si vous tentez de le faire sans savoir ce que vous faites, le pytest
package public déclenche généralement des exceptions obscures lors de l'exécution. Merci beaucoup, py.test. Une architecture vraiment robuste que vous avez obtenue.
- Même lorsque vous savez comment corriger de
_pytest
manière sécurisée l'API privée de manière simple , vous devez le faire avant d' exécuter le pytest
package public exécuté par la py.test
commande externe . Vous ne pouvez pas le faire dans un plugin (par exemple, un conftest
module de niveau supérieur dans votre suite de tests). Au moment où py.test parvient paresseusement à importer dynamiquement votre plug-in, toute classe py.test que vous vouliez faire un patch de singe a depuis longtemps été instanciée - et vous n'avez pas accès à cette instance. Cela implique que, si vous souhaitez que votre patch de singe soit appliqué de manière significative, vous ne pouvez plus exécuter la py.test
commande externe en toute sécurité . Au lieu de cela, vous devez envelopper l'exécution de cette commande avec un setuptools personnalisétest
commande que (dans l'ordre):
- Monkey patche l'
_pytest
API privée .
- Appelle la
pytest.main()
fonction publique pour exécuter la py.test
commande.
Cette réponse monkey-patches py.test -s
et les --capture=no
options pour capturer stderr mais pas stdout. Par défaut, ces options ne capturent ni stderr ni stdout. Ce n'est pas tout à fait évident, bien sûr. Mais chaque grand voyage commence par une préquelle fastidieuse que tout le monde oublie dans cinq ans.
Pourquoi faire ceci? Je vais maintenant vous le dire. Ma suite de tests pilotée par py.test contient des tests fonctionnels lents. L'affichage de la sortie standard de ces tests est utile et rassurant, empêchant leycec d'atteindre killall -9 py.test
quand encore un autre test fonctionnel de longue durée ne fait rien pendant des semaines. Cependant, l'affichage du stderr de ces tests empêche py.test de signaler les retraits d'exception en cas d'échecs de test. Ce qui est complètement inutile. Par conséquent, nous contraignons py.test à capturer stderr mais pas stdout.
Avant d'y arriver, cette réponse suppose que vous disposez déjà d'une test
commande setuptools personnalisée appelant py.test. Si ce n'est pas le cas, consultez la sous-section Intégration manuelle de la page bien écrite des bonnes pratiques de py.test .
Ne pas installer pytest-runner , un setuptools tiers fournissant un plugin d' setuptools personnalisés test
commande demandant également py.test. Si pytest-runner est déjà installé, vous devrez probablement désinstaller ce package pip3, puis adopter l'approche manuelle liée à ci-dessus.
En supposant que vous ayez suivi les instructions de l' intégration manuelle soulignées ci-dessus, votre base de code devrait maintenant contenir une PyTest.run_tests()
méthode. Modifiez cette méthode pour qu'elle ressemble à:
class PyTest(TestCommand):
.
.
.
def run_tests(self):
# Import the public "pytest" package *BEFORE* the private "_pytest"
# package. While importation order is typically ignorable, imports can
# technically have side effects. Tragicomically, that is the case here.
# Importing the public "pytest" package establishes runtime
# configuration required by submodules of the private "_pytest" package.
# The former *MUST* always be imported before the latter. Failing to do
# so raises obtuse exceptions at runtime... which is bad.
import pytest
from _pytest.capture import CaptureManager, FDCapture, MultiCapture
# If the private method to be monkey-patched no longer exists, py.test
# is either broken or unsupported. In either case, raise an exception.
if not hasattr(CaptureManager, '_getcapture'):
from distutils.errors import DistutilsClassError
raise DistutilsClassError(
'Class "pytest.capture.CaptureManager" method _getcapture() '
'not found. The current version of py.test is either '
'broken (unlikely) or unsupported (likely).'
)
# Old method to be monkey-patched.
_getcapture_old = CaptureManager._getcapture
# New method applying this monkey-patch. Note the use of:
#
# * "out=False", *NOT* capturing stdout.
# * "err=True", capturing stderr.
def _getcapture_new(self, method):
if method == "no":
return MultiCapture(
out=False, err=True, in_=False, Capture=FDCapture)
else:
return _getcapture_old(self, method)
# Replace the old with the new method.
CaptureManager._getcapture = _getcapture_new
# Run py.test with all passed arguments.
errno = pytest.main(self.pytest_args)
sys.exit(errno)
Pour activer ce patch de singe, exécutez py.test comme suit:
python setup.py test -a "-s"
Stderr mais pas stdout seront désormais capturés. Nifty!
L'extension du patch de singe ci-dessus au départ de stdout et stderr est laissée au lecteur comme exercice avec un baril plein de temps libre.