First implementation
This commit is contained in:
parent
5c8c9e7ed1
commit
e78bcbaa00
@ -0,0 +1,3 @@
|
|||||||
|
Demonstration of the use of "conditional cleanups" in `pytest`, as explored further in [this blog post](https://blog.scubbo.org/posts/conditional-cleanups-in-pytest).
|
||||||
|
|
||||||
|
Run `pytest`, and observe that the tempfile is cleaned up for the passing test, but persisted for the failing test.
|
0
lib/__init__.py
Normal file
0
lib/__init__.py
Normal file
5
lib/main.py
Normal file
5
lib/main.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import pathlib
|
||||||
|
|
||||||
|
# Very simple method as placeholder for logic being tested.
|
||||||
|
def write_content_to_file(content: str, path: pathlib.Path) -> None:
|
||||||
|
path.write_text(content)
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
45
tests/conftest.py
Normal file
45
tests/conftest.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/69281822/how-to-only-run-a-pytest-fixture-cleanup-on-test-error-or-failure,
|
||||||
|
# Though syntax appears to have changed
|
||||||
|
# https://docs.pytest.org/en/latest/example/simple.html#making-test-result-information-available-in-fixtures
|
||||||
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
|
def pytest_runtest_makereport(item, call):
|
||||||
|
# execute all other hooks to obtain the report object
|
||||||
|
outcome = yield
|
||||||
|
|
||||||
|
# TODO - we may care about more than just a binary result!
|
||||||
|
# (i.e. a skipped test is neither passed nor failed...probably?)
|
||||||
|
setattr(
|
||||||
|
item,
|
||||||
|
"rep_" + outcome.get_result().when + "_passed",
|
||||||
|
outcome.get_result().passed,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Cleanups(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.success_cleanups = []
|
||||||
|
self.failure_cleanups = []
|
||||||
|
|
||||||
|
def add_success(self, success_cleanup: Callable[[], None]):
|
||||||
|
self.success_cleanups.append(success_cleanup)
|
||||||
|
|
||||||
|
def add_failure(self, failure_cleanup: Callable[[], None]):
|
||||||
|
self.failure_cleanups.append(failure_cleanup)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def cleanups(request):
|
||||||
|
cleanups = Cleanups()
|
||||||
|
yield cleanups
|
||||||
|
|
||||||
|
if request.node.rep_call_passed:
|
||||||
|
cleanups = cleanups.success_cleanups
|
||||||
|
else:
|
||||||
|
cleanups = cleanups.failure_cleanups
|
||||||
|
if cleanups:
|
||||||
|
for cleanup in cleanups[::-1]: # Apply in reverse order
|
||||||
|
cleanup()
|
39
tests/test_basic.py
Normal file
39
tests/test_basic.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
from lib.main import write_content_to_file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("test_input", ["Expected content", "Incorrect content"])
|
||||||
|
def test(test_input, temp_file_path, cleanups):
|
||||||
|
|
||||||
|
write_content_to_file(test_input, temp_file_path)
|
||||||
|
|
||||||
|
def in_test_print_out_on_test_failure():
|
||||||
|
print(f'(In-test cleanup) Test using tempfile {temp_file_path} failed')
|
||||||
|
cleanups.add_failure(in_test_print_out_on_test_failure)
|
||||||
|
|
||||||
|
assert temp_file_path.read_text() == "Expected content"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def temp_file_path(cleanups) -> pathlib.Path:
|
||||||
|
# In real production use, you'd be more likely to use methods from the `tempfile` library
|
||||||
|
# (https://docs.python.org/3/library/tempfile.html) directly - but I'm implementing this fixture by-hand to
|
||||||
|
# demonstrate that the `cleanups` object can handle cleanup-methods being attached from within a fixture.
|
||||||
|
f = tempfile.NamedTemporaryFile(delete=False, dir='.')
|
||||||
|
path = pathlib.Path(f.name)
|
||||||
|
|
||||||
|
def delete_temp_file_on_test_success():
|
||||||
|
path.unlink()
|
||||||
|
cleanups.add_success(delete_temp_file_on_test_success)
|
||||||
|
|
||||||
|
def print_out_temp_file_path_on_test_failure():
|
||||||
|
print(f'Test failed involving tempfile {path}')
|
||||||
|
cleanups.add_failure(print_out_temp_file_path_on_test_failure)
|
||||||
|
|
||||||
|
return path
|
Loading…
x
Reference in New Issue
Block a user