Search code examples
pythonunit-testingtestingpytesttemp

Pytest Fixture Fails to Clean Up Directories for Specific Test Cases


I am using a Pytest fixture to clean up temporary files and directories created during tests. The fixture works for most test cases, but it fails to completely delete directories created in the first two parameterized tests. Here's the simplified fixture:

import pytest
import os
import shutil

@pytest.fixture(scope="session")
def cleanup_environment():
    temp_paths = []  # List to track temporary paths

    yield temp_paths  # Provide the list to tests

    # Sort paths by directory depth (deepest first)
    temp_paths_sorted = sorted(temp_paths, key=lambda p: p.count(os.sep), reverse=True)

    for temp_path in temp_paths_sorted:
        if os.path.exists(temp_path):
            try:
                if os.path.isfile(temp_path):
                    os.remove(temp_path)
                elif os.path.isdir(temp_path):
                    shutil.rmtree(temp_path, ignore_errors=True)
            except Exception as e:
                print(f"Failed to remove {temp_path}: {e}")

    for temp_path in temp_paths_sorted:
        parent_dir = os.path.dirname(temp_path)
        while parent_dir and os.path.exists(parent_dir) and not os.listdir(parent_dir):
            try:
                os.rmdir(parent_dir)
                parent_dir = os.path.dirname(parent_dir)
            except Exception as e:
                print(f"Failed to remove parent directory {parent_dir}: {e}")
                break

Here are the test cases:

Works (Directories and files are cleaned up):

def test_create_and_delete_file(cleanup_environment, tmpdir):
    temp_file = tmpdir.join("temp_file.txt")
    temp_file.write("Temporary content")
    cleanup_environment.append(str(temp_file))
    assert temp_file.check(file=True)

def test_create_and_delete_dir(cleanup_environment, tmpdir):
    temp_dir = tmpdir.mkdir("temp_dir")
    cleanup_environment.append(str(temp_dir))
    assert temp_dir.check(dir=True)

Fails (Directories from these tests are not fully cleaned up):

@pytest.mark.parametrize("test_case", ["case1", "case2"])
def test_parameterized_cases(cleanup_environment, tmpdir, test_case):
    temp_dir = tmpdir.mkdir(f"temp_dir_{test_case}")
    cleanup_environment.append(str(temp_dir))
    assert temp_dir.check(dir=True)

In the parameterized test cases (case1 and case2), the directories created are not fully deleted, even though they are appended to cleanup_environment.

How can I ensure that all directories, including those from parameterized tests, are cleaned up properly?


Solution

  • Why are you bothering to track individual files and/or directories like this? It would make more sense to create a temporary directory per session, and then remove it entirely when you're done. This is easy if you use your own fixture to create temporary directories, rather than the pytest tmpdir fixture (which is explicitly designed to not clean up after your tests, so that you can inspect test artifacts after the test run is complete).

    Something like this:

    import pytest
    import tempfile
    from pathlib import Path
    
    
    @pytest.fixture(scope="session")
    def clean_tmpdir():
        # a tempfile.TemporaryDirectory() object deletes itself when it
        # goes out of scope.
        with tempfile.TemporaryDirectory() as tmpdir:
            yield Path(tmpdir)
    
    
    @pytest.mark.parametrize("test_case", ["case1", "case2"])
    def test_parameterized_cases(clean_tmpdir: Path, test_case: str):
        print(f"using directory: {clean_tmpdir}")
        dir = clean_tmpdir / f"temp_dir_{test_case}"
        dir.mkdir()
        assert dir.is_dir()