Verwaltung der pytest-Ausgabe¶
Ändern der Python-Traceback-Ausgabe¶
Beispiele für die Änderung der Traceback-Ausgabe
pytest --showlocals # show local variables in tracebacks
pytest -l # show local variables (shortcut)
pytest --no-showlocals # hide local variables (if addopts enables them)
pytest --capture=fd # default, capture at the file descriptor level
pytest --capture=sys # capture at the sys level
pytest --capture=no # don't capture
pytest -s # don't capture (shortcut)
pytest --capture=tee-sys # capture to logs but also output to sys level streams
pytest --tb=auto # (default) 'long' tracebacks for the first and last
# entry, but 'short' style for the other entries
pytest --tb=long # exhaustive, informative traceback formatting
pytest --tb=short # shorter traceback format
pytest --tb=line # only one line per failure
pytest --tb=native # Python standard library formatting
pytest --tb=no # no traceback at all
Die Option --full-trace verursacht die Ausgabe sehr langer Spuren bei Fehlern (länger als --tb=long). Sie stellt auch sicher, dass bei einer KeyboardInterrupt (Strg+C) eine Stapelspur ausgegeben wird. Dies ist sehr nützlich, wenn die Tests zu lange dauern und Sie sie mit Strg+C unterbrechen, um herauszufinden, wo die Tests hängen. Standardmäßig wird keine Ausgabe angezeigt (da pytest KeyboardInterrupt abfängt). Durch die Verwendung dieser Option stellen Sie sicher, dass eine Spur angezeigt wird.
Ausführlichkeit¶
Beispiele für die Änderung der Ausführlichkeit der Ausgabe
pytest --quiet # quiet - less verbose - mode
pytest -q # quiet - less verbose - mode (shortcut)
pytest -v # increase verbosity, display individual test names
pytest -vv # more verbose, display more details from the test output
pytest -vvv # not a standard , but may be used for even more detail in certain setups
Das Flag -v steuert die Ausführlichkeit der pytest-Ausgabe in verschiedenen Aspekten: Testlauf-Fortschritt, Assertionsdetails bei fehlerhaften Tests, Fixture-Details mit --fixtures usw.
Betrachten Sie diese einfache Datei
# content of test_verbosity_example.py
def test_ok():
pass
def test_words_fail():
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
assert fruits1 == fruits2
def test_numbers_fail():
number_to_text1 = {str(x): x for x in range(5)}
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
assert number_to_text1 == number_to_text2
def test_long_text_fail():
long_text = "Lorem ipsum dolor sit amet " * 10
assert "hello world" in long_text
Die normale Ausführung von pytest ergibt diese Ausgabe (wir überspringen die Kopfzeile, um uns auf den Rest zu konzentrieren)
$ pytest --no-header
=========================== test session starts ============================
collected 4 items
test_verbosity_example.py .FFF [100%]
================================= FAILURES =================================
_____________________________ test_words_fail ______________________________
def test_words_fail():
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
> assert fruits1 == fruits2
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
E
E At index 2 diff: 'grapes' != 'orange'
E Use -v to get more diff
test_verbosity_example.py:8: AssertionError
____________________________ test_numbers_fail _____________________________
def test_numbers_fail():
number_to_text1 = {str(x): x for x in range(5)}
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
> assert number_to_text1 == number_to_text2
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
E
E Omitting 1 identical items, use -vv to show
E Left contains 4 more items:
E {'1': 1, '2': 2, '3': 3, '4': 4}
E Right contains 4 more items:
E {'10': 10, '20': 20, '30': 30, '40': 40}
E Use -v to get more diff
test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________
def test_long_text_fail():
long_text = "Lorem ipsum dolor sit amet " * 10
> assert "hello world" in long_text
E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ips... sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
test_verbosity_example.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
======================= 3 failed, 1 passed in 0.12s ========================
Beachten Sie, dass
Jeder Test innerhalb der Datei wird durch ein einzelnes Zeichen in der Ausgabe dargestellt:
.für erfolgreich,Ffür fehlgeschlagen.test_words_failist fehlgeschlagen, und wir sehen eine kurze Zusammenfassung, die angibt, dass sich der Index 2 der beiden Listen unterscheidet.test_numbers_failist fehlgeschlagen, und wir sehen eine Zusammenfassung der Unterschiede zwischen links und rechts bei Dictionary-Elementen. Identische Elemente werden weggelassen.test_long_text_failist fehlgeschlagen, und die rechte Seite derin-Anweisung wird mit...`abgeschnitten, da sie länger als ein interner Schwellenwert (derzeit 240 Zeichen) ist.
Nun können wir die Ausführlichkeit von pytest erhöhen
$ pytest --no-header -v
=========================== test session starts ============================
collecting ... collected 4 items
test_verbosity_example.py::test_ok PASSED [ 25%]
test_verbosity_example.py::test_words_fail FAILED [ 50%]
test_verbosity_example.py::test_numbers_fail FAILED [ 75%]
test_verbosity_example.py::test_long_text_fail FAILED [100%]
================================= FAILURES =================================
_____________________________ test_words_fail ______________________________
def test_words_fail():
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
> assert fruits1 == fruits2
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
E
E At index 2 diff: 'grapes' != 'orange'
E
E Full diff:
E [
E 'banana',
E 'apple',...
E
E ...Full output truncated (7 lines hidden), use '-vv' to show
test_verbosity_example.py:8: AssertionError
____________________________ test_numbers_fail _____________________________
def test_numbers_fail():
number_to_text1 = {str(x): x for x in range(5)}
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
> assert number_to_text1 == number_to_text2
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
E
E Omitting 1 identical items, use -vv to show
E Left contains 4 more items:
E {'1': 1, '2': 2, '3': 3, '4': 4}
E Right contains 4 more items:
E {'10': 10, '20': 20, '30': 30, '40': 40}
E ...
E
E ...Full output truncated (16 lines hidden), use '-vv' to show
test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________
def test_long_text_fail():
long_text = "Lorem ipsum dolor sit amet " * 10
> assert "hello world" in long_text
E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
test_verbosity_example.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
======================= 3 failed, 1 passed in 0.12s ========================
Beachten Sie nun, dass
Jeder Test innerhalb der Datei erhält in der Ausgabe eine eigene Zeile.
test_words_failzeigt nun die beiden fehlerhaften Listen vollständig an, zusätzlich dazu, welcher Index sich unterscheidet.test_numbers_failzeigt nun einen Text-Diff der beiden Dictionaries, gekürzt.test_long_text_failkürzt die rechte Seite derin-Anweisung nicht mehr, da der interne Schwellenwert für die Kürzung jetzt höher ist (derzeit 2400 Zeichen).
Nun erhöhen wir die Ausführlichkeit noch weiter
$ pytest --no-header -vv
=========================== test session starts ============================
collecting ... collected 4 items
test_verbosity_example.py::test_ok PASSED [ 25%]
test_verbosity_example.py::test_words_fail FAILED [ 50%]
test_verbosity_example.py::test_numbers_fail FAILED [ 75%]
test_verbosity_example.py::test_long_text_fail FAILED [100%]
================================= FAILURES =================================
_____________________________ test_words_fail ______________________________
def test_words_fail():
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
> assert fruits1 == fruits2
E AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']
E
E At index 2 diff: 'grapes' != 'orange'
E
E Full diff:
E [
E 'banana',
E 'apple',
E - 'orange',
E ? ^ ^^
E + 'grapes',
E ? ^ ^ +
E 'melon',
E 'kiwi',
E ]
test_verbosity_example.py:8: AssertionError
____________________________ test_numbers_fail _____________________________
def test_numbers_fail():
number_to_text1 = {str(x): x for x in range(5)}
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
> assert number_to_text1 == number_to_text2
E AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
E
E Common items:
E {'0': 0}
E Left contains 4 more items:
E {'1': 1, '2': 2, '3': 3, '4': 4}
E Right contains 4 more items:
E {'10': 10, '20': 20, '30': 30, '40': 40}
E
E Full diff:
E {
E '0': 0,
E - '10': 10,
E ? - -
E + '1': 1,
E - '20': 20,
E ? - -
E + '2': 2,
E - '30': 30,
E ? - -
E + '3': 3,
E - '40': 40,
E ? - -
E + '4': 4,
E }
test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________
def test_long_text_fail():
long_text = "Lorem ipsum dolor sit amet " * 10
> assert "hello world" in long_text
E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
test_verbosity_example.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_verbosity_example.py::test_words_fail - AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']
At index 2 diff: 'grapes' != 'orange'
Full diff:
[
'banana',
'apple',
- 'orange',
? ^ ^^
+ 'grapes',
? ^ ^ +
'melon',
'kiwi',
]
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
Common items:
{'0': 0}
Left contains 4 more items:
{'1': 1, '2': 2, '3': 3, '4': 4}
Right contains 4 more items:
{'10': 10, '20': 20, '30': 30, '40': 40}
Full diff:
{
'0': 0,
- '10': 10,
? - -
+ '1': 1,
- '20': 20,
? - -
+ '2': 2,
- '30': 30,
? - -
+ '3': 3,
- '40': 40,
? - -
+ '4': 4,
}
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
======================= 3 failed, 1 passed in 0.12s ========================
Beachten Sie nun, dass
Jeder Test innerhalb der Datei erhält in der Ausgabe eine eigene Zeile.
test_words_failgibt in diesem Fall die gleiche Ausgabe wie zuvor aus.test_numbers_failzeigt nun einen vollständigen Text-Diff der beiden Dictionaries.test_long_text_failkürzt ebenfalls nicht mehr auf der rechten Seite wie zuvor, aber jetzt kürzt pytest keinen Text mehr, unabhängig von seiner Größe.
Das waren Beispiele dafür, wie sich die Ausführlichkeit auf die normale Testlauf-Ausgabe auswirkt, aber Ausführlichkeit wird auch in anderen Situationen verwendet, zum Beispiel werden Ihnen sogar Fixtures angezeigt, die mit _ beginnen, wenn Sie pytest --fixtures -v verwenden.
Höhere Ausführlichkeitsstufen (-vvv, -vvvv, ...) werden unterstützt, haben aber derzeit keine Auswirkung auf pytest selbst. Einige Plugins können jedoch höhere Ausführlichkeitsstufen nutzen.
Feingranulare Ausführlichkeit¶
Zusätzlich zur Angabe der anwendungsweiten Ausführlichkeitsstufe ist es möglich, bestimmte Aspekte unabhängig voneinander zu steuern. Dies geschieht durch Setzen einer Ausführlichkeitsstufe in der Konfigurationsdatei für den spezifischen Aspekt der Ausgabe.
verbosity_assertions: Steuert, wie ausführlich die Assertionsausgabe bei der Ausführung von pytest sein soll. Die Ausführung von pytest --no-header mit einem Wert von 2 hätte die gleiche Ausgabe wie im vorherigen Beispiel, aber jeder Test in der Datei wird durch ein einzelnes Zeichen in der Ausgabe dargestellt.
verbosity_test_cases: Steuert, wie ausführlich die Testausführungs-Ausgabe bei der Ausführung von pytest sein soll. Die Ausführung von pytest --no-header mit einem Wert von 2 hätte die gleiche Ausgabe wie im ersten Ausführlichkeitsbeispiel, aber jeder Test in der Datei erhält seine eigene Zeile in der Ausgabe.
Erstellung eines detaillierten Zusammenfassungsberichts¶
Das Flag -r kann verwendet werden, um am Ende des Testlaufs eine "kurze Zusammenfassung der Testergebnisse" anzuzeigen, was es bei großen Testsuiten erleichtert, ein klares Bild von allen Fehlern, Überspringungen, xfails usw. zu erhalten.
Standardmäßig ist es auf fE eingestellt, um Fehler und Ausnahmen aufzulisten.
Beispiel
# content of test_example.py
import pytest
@pytest.fixture
def error_fixture():
assert 0
def test_ok():
print("ok")
def test_fail():
assert 0
def test_error(error_fixture):
pass
def test_skip():
pytest.skip("skipping this test")
def test_xfail():
pytest.xfail("xfailing this test")
@pytest.mark.xfail(reason="always xfail")
def test_xpass():
pass
$ pytest -ra
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 6 items
test_example.py .FEsxX [100%]
================================== ERRORS ==================================
_______________________ ERROR at setup of test_error _______________________
@pytest.fixture
def error_fixture():
> assert 0
E assert 0
test_example.py:6: AssertionError
================================= FAILURES =================================
________________________________ test_fail _________________________________
def test_fail():
> assert 0
E assert 0
test_example.py:14: AssertionError
================================= XPASSES ==================================
========================= short test summary info ==========================
SKIPPED [1] test_example.py:22: skipping this test
XFAIL test_example.py::test_xfail - xfailing this test
XPASS test_example.py::test_xpass - always xfail
ERROR test_example.py::test_error - assert 0
FAILED test_example.py::test_fail - assert 0
== 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
Die -r Option akzeptiert eine Anzahl von Zeichen danach, wobei das oben verwendete a "alle außer erfolgreichen" bedeutet.
Hier ist die vollständige Liste der verfügbaren Zeichen, die verwendet werden können
f- fehlgeschlagen
E- Fehler
s- übersprungen
x- xfailed
X- xpassed
p- erfolgreich
P- erfolgreich mit Ausgabe
Spezielle Zeichen für (De-)Auswahl von Gruppen
a- alle außerpP
A- alle
N- keine, dies kann verwendet werden, um nichts anzuzeigen (dafEder Standard ist)
Mehrere Zeichen können verwendet werden, so dass Sie zum Beispiel nur fehlgeschlagene und übersprungene Tests sehen können, indem Sie Folgendes ausführen
$ pytest -rfs
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 6 items
test_example.py .FEsxX [100%]
================================== ERRORS ==================================
_______________________ ERROR at setup of test_error _______________________
@pytest.fixture
def error_fixture():
> assert 0
E assert 0
test_example.py:6: AssertionError
================================= FAILURES =================================
________________________________ test_fail _________________________________
def test_fail():
> assert 0
E assert 0
test_example.py:14: AssertionError
========================= short test summary info ==========================
FAILED test_example.py::test_fail - assert 0
SKIPPED [1] test_example.py:22: skipping this test
== 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
Die Verwendung von p listet die erfolgreichen Tests auf, während P einen zusätzlichen Abschnitt "PASSES" mit den Tests hinzufügt, die erfolgreich waren, aber erfasste Ausgaben hatten.
$ pytest -rpP
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 6 items
test_example.py .FEsxX [100%]
================================== ERRORS ==================================
_______________________ ERROR at setup of test_error _______________________
@pytest.fixture
def error_fixture():
> assert 0
E assert 0
test_example.py:6: AssertionError
================================= FAILURES =================================
________________________________ test_fail _________________________________
def test_fail():
> assert 0
E assert 0
test_example.py:14: AssertionError
================================== PASSES ==================================
_________________________________ test_ok __________________________________
--------------------------- Captured stdout call ---------------------------
ok
========================= short test summary info ==========================
PASSED test_example.py::test_ok
== 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
Hinweis
Standardmäßig werden parametrisierte Varianten von übersprungenen Tests zusammengefasst, wenn sie denselben Überspringungsgrund haben. Sie können --no-fold-skipped verwenden, um jeden übersprungenen Test separat auszugeben.
Ändern der Kürzungsgrenzen¶
Standardmäßige Kürzungsgrenzen sind 8 Zeilen oder 640 Zeichen, je nachdem, was zuerst eintritt. Um benutzerdefinierte Kürzungsgrenzen festzulegen, können Sie die folgenden Optionen in der Konfigurationsdatei verwenden
[pytest]
truncation_limit_lines = 10
truncation_limit_chars = 90
[pytest]
truncation_limit_lines = 10
truncation_limit_chars = 90
Dies führt dazu, dass pytest die Assertions auf 10 Zeilen oder 90 Zeichen kürzt, je nachdem, was zuerst eintritt.
Das Setzen von truncation_limit_lines und truncation_limit_chars auf 0 deaktiviert die Kürzung. Das Setzen nur eines dieser Werte deaktiviert jedoch einen Kürzungsvorgang, lässt aber den anderen intakt.
Erstellung von Dateien im JUnitXML-Format¶
Um Ergebnisdateien zu erstellen, die von Jenkins oder anderen Continuous Integration-Servern gelesen werden können, verwenden Sie diese Eingabeaufforderung
pytest --junit-xml=path
um eine XML-Datei unter path zu erstellen.
Um den Namen des Stamm-Testsuite-XML-Elements festzulegen, können Sie die Option junit_suite_name in Ihrer Konfigurationsdatei festlegen
[pytest]
junit_suite_name = "my_suite"
[pytest]
junit_suite_name = my_suite
Hinzugefügt in Version 4.0.
Die JUnit XML-Spezifikation scheint anzugeben, dass das Attribut "time" die gesamten Testausführungszeiten, einschließlich Einrichtung und Abbau, melden soll (1, 2). Dies ist das Standardverhalten von pytest. Um nur Aufrufzeiten zu melden, konfigurieren Sie die Option junit_duration_report wie folgt
[pytest]
junit_duration_report = "call"
[pytest]
junit_duration_report = call
record_property¶
Wenn Sie zusätzliche Informationen für einen Test protokollieren möchten, können Sie die Fixture record_property verwenden
def test_function(record_property):
record_property("example_key", 1)
assert True
Dies fügt eine zusätzliche Eigenschaft example_key="1" zum generierten testcase-Tag hinzu
<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
<properties>
<property name="example_key" value="1" />
</properties>
</testcase>
Alternativ können Sie diese Funktionalität mit benutzerdefinierten Markern integrieren
# content of conftest.py
def pytest_collection_modifyitems(session, config, items):
for item in items:
for marker in item.iter_markers(name="test_id"):
test_id = marker.args[0]
item.user_properties.append(("test_id", test_id))
Und in Ihren Tests
# content of test_function.py
import pytest
@pytest.mark.test_id(1501)
def test_function():
assert True
Führt zu
<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
<properties>
<property name="test_id" value="1501" />
</properties>
</testcase>
Warnung
Bitte beachten Sie, dass die Verwendung dieser Funktion die Schemavalidierung für das neueste JUnitXML-Schema bricht. Dies kann ein Problem sein, wenn es mit einigen CI-Servern verwendet wird.
record_xml_attribute¶
Um ein zusätzliches XML-Attribut zu einem testcase-Element hinzuzufügen, können Sie die Fixture record_xml_attribute verwenden. Dies kann auch verwendet werden, um vorhandene Werte zu überschreiben
def test_function(record_xml_attribute):
record_xml_attribute("assertions", "REQ-1234")
record_xml_attribute("classname", "custom_classname")
print("hello world")
assert True
Im Gegensatz zu record_property wird hier kein neues Kindelement hinzugefügt. Stattdessen wird ein Attribut assertions="REQ-1234" innerhalb des generierten testcase-Tags hinzugefügt und der Standard-classname mit "classname=custom_classname" überschrieben.
<testcase classname="custom_classname" file="test_function.py" line="0" name="test_function" time="0.003" assertions="REQ-1234">
<system-out>
hello world
</system-out>
</testcase>
Warnung
record_xml_attribute ist eine experimentelle Funktion, und ihre Schnittstelle kann in zukünftigen Versionen durch etwas Leistungsfähigeres und Allgemeineres ersetzt werden. Die Funktionalität an sich wird jedoch beibehalten.
Die Verwendung über record_xml_property kann bei der Verwendung von CI-Tools zur Analyse des XML-Berichts helfen. Einige Parser sind jedoch sehr streng bezüglich der zulässigen Elemente und Attribute. Viele Tools verwenden ein XSD-Schema (wie im folgenden Beispiel), um eingehende XML-Daten zu validieren. Stellen Sie sicher, dass Sie Attributnamen verwenden, die von Ihrem Parser zugelassen sind.
Unten sehen Sie das Schema, das von Jenkins zur Validierung des XML-Berichts verwendet wird
<xs:element name="testcase">
<xs:complexType>
<xs:sequence>
<xs:element ref="skipped" minOccurs="0" maxOccurs="1"/>
<xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="assertions" type="xs:string" use="optional"/>
<xs:attribute name="time" type="xs:string" use="optional"/>
<xs:attribute name="classname" type="xs:string" use="optional"/>
<xs:attribute name="status" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>
Warnung
Bitte beachten Sie, dass die Verwendung dieser Funktion die Schemavalidierung für das neueste JUnitXML-Schema bricht. Dies kann ein Problem sein, wenn es mit einigen CI-Servern verwendet wird.
record_testsuite_property¶
Hinzugefügt in Version 4.5.
Wenn Sie einen Eigenschaften-Knoten auf der Testsuite-Ebene hinzufügen möchten, der Eigenschaften enthalten kann, die für alle Tests relevant sind, können Sie die Fixture record_testsuite_property mit Session-Scope verwenden
Die Fixture record_testsuite_property mit Session-Scope kann verwendet werden, um für alle Tests relevante Eigenschaften hinzuzufügen.
import pytest
@pytest.fixture(scope="session", autouse=True)
def log_global_env_facts(record_testsuite_property):
record_testsuite_property("ARCH", "PPC")
record_testsuite_property("STORAGE_TYPE", "CEPH")
class TestMe:
def test_foo(self):
assert True
Die Fixture ist ein aufrufbares Objekt, das name und value eines <property>-Tags empfängt, das auf der Testsuite-Ebene des generierten XML hinzugefügt wird.
<testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.006">
<properties>
<property name="ARCH" value="PPC"/>
<property name="STORAGE_TYPE" value="CEPH"/>
</properties>
<testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
</testsuite>
name muss ein String sein, value wird in einen String konvertiert und korrekt XML-escaped.
Das generierte XML ist mit dem neuesten xunit-Standard kompatibel, im Gegensatz zu record_property und record_xml_attribute.
Senden des Testberichts an einen Online-Pastebin-Dienst¶
Erstellung einer URL für jeden Testfehler:
pytest --pastebin=failed
Dies übermittelt die Testlaufinformationen an einen entfernten Paste-Dienst und stellt für jeden Fehler eine URL bereit. Sie können Tests wie gewohnt auswählen oder beispielsweise -x hinzufügen, wenn Sie nur einen bestimmten Fehler senden möchten.
Erstellung einer URL für ein vollständiges Testlauf-Log:
pytest --pastebin=all
Derzeit ist nur das Einfügen auf dem https://bpaste.net/-Dienst implementiert.
Geändert in Version 5.2.
Wenn die Erstellung der URL aus irgendeinem Grund fehlschlägt, wird eine Warnung generiert, anstatt den gesamten Testlauf abzubrechen.