retain-on-failure discards trace/video if teardown/setup fails in fixtures
From what I can see, if a fixture fails in setup/teardown, the pytest --output .out --tracing retain-on-failure discards the trace. And at least sometimes the user will want the trace to be kept.
The code only looks at whether the rep_call has failed. https://github.com/microsoft/playwright-pytest/blob/main/pytest_playwright/pytest_playwright.py#L224-L226
But that ignores issues in the setup/teardown, e.g. pytests the example code
# content of conftest.py
import pytest
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
outcome = yield
rep = outcome.get_result()
# set a report attribute for each phase of a call, which can
# be "setup", "call", "teardown"
setattr(item, "rep_" + rep.when, rep)
@pytest.fixture
def something(request):
yield
# request.node is an "item" because we use the default
# "function" scope
if request.node.rep_setup.failed:
print("setting up a test failed!", request.node.nodeid)
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
print("executing test failed", request.node.nodeid)
So if a user writes a fixture, that does something with a page in it's setup or teardown, and that setup or teardown code fails, the trace/video will be discarded. That's because at this point pytest will set rep_setup.failed (or rep_teardown) to be true, rather than rep_call.failed, I believe. Docs for the runtest_protocol
@pytest.fixture()
def logged_in(page: Page) -> Page:
"""Log in"""
log_in_flow(page)
yield page
log_out_flow(page)
Thank you for your bug report!
Do you have a solution in mind how to solve it? Because currently we create e.g. tracing after the yield of the context fixture. So the teardown error of the "problematic" fixture happens after that.
Do I have a solution? Not really. My initial thoughts were altering the line to include rep_setup.failed/rep_teardown.failed:
https://github.com/microsoft/playwright-pytest/blob/main/pytest_playwright/pytest_playwright.py#L226
So this
# If requst.node is missing rep_call, then some error happened during execution
# that prevented teardown, but should still be counted as a failure
failed = request.node.rep_call.failed if hasattr(request.node, "rep_call") else True
would change to something like this:
# If requst.node is missing rep_call, then some error happened during execution
# that prevented teardown, but should still be counted as a failure
failed = any(
[request.node.rep_call.failed if hasattr(request.node, "rep_call") else True],
[request.node.rep_setup.failed if hasattr(request.node, "rep_setup") else True],
[request.node.rep_teardown.failed if hasattr(request.node, "rep_teardown") else True],
)
Sorry, just a quick guess at where I might alter the code. I'm not familiar with exactly how pytest alter's the request object. But I was hoping maybe just expanding the retain-on-failure to include errors during setup/teardown.
I tried that, but it fails as of today after the context was closed. So it won't work. We'd need a different point where we hook in and close the context and manipulate fixture teardown order.
@mxschmitt Looks like this is more general problem: any test artifacts relay on failed = request.node.rep_call.failed if hasattr(request.node, "rep_call") else True logic under the hood. So we miss rep_setup/rep_teardown that's it.
Hi Thanks! Are you interested in contributing a fix with a test?
yes, I can give a shot)
@mxschmitt I have tried a lot of things using TDD. Also I found related bug on pytest https://github.com/pytest-dev/pytest/issues/9909. For now setup phase fixed, but teardown doesn't even know about its state (failed or passed) because of context implementation. I create draft see #207 to show off some stuff (look at test_artifacts_on_teardown). So, I suggest one of the next solution vector:
- save artifacts, then check the final test state under
pytest_runtest_makereportand ifteardownwas passed, remove the output (overhead) - get n parse the trace (I got the needed info under
teardown_exactphase see attached pic, but don't know how to get one, ideas welcome Max) to figure out is there exception or not to set the special flag (failed_xteardownviaconfig.teardown) forcontextto process or not screenshots, etc.
@mxschmitt I solved all the problems above, can you review?