Skip to main content

Unittest Scenarios

Tests Coverage PyPi PyPI - Python Version

This library adds "scenario-based testing" to Pytest via unittest.TestCase mixins. A "scenario-based test" is a test that is defined by an initial and final state of files. This is useful for testing things like data pipelines/workflows where inputs/outputs are file based, or any other type of test that is suited to be represented by a file structure.

This package includes methods for recursively comparing directories, archives, text files, and binary files in a cross-platform friendly way. This library also has flexible tools for configuring isolated testing environments, again useful for data pipelines like snakemake that might depend on files present in the working directory.

Installation

pip install unittest-scenarios

Usage

FileCmpMixin

This class provides several methods for checking the contents of files or directories to see if they are equal. The idea is to be able to recursively compare files and paths in a platform-agnostic way. Provides the following methods:

Method Usage
assertDirectoryContentsEqual Recursively compares contents of directories, detecting appropriate methods
assertDirectoryContentsNotEqual Negative of above
assertArchiveContentsEqual Temporarily extracts both archives and compares them using assertDirectoryContentsEqual
assertArchiveContentsNotEqual Negative of above
assertTextFilesEqual Opens files with universal line endings and compares line by line
assertTextFilesNotEqual Negative of above
assertFileHashesEqual Compares hash of file contents with customizable hash function
assertFileHashesNotEqual Negative of above
assertPathContentsEqual Automatically detects file types and recursively compares with appropriate method
assertPathContentsNotEqual Negative of above
class MyTestCase(FileCmpMixin, unittest.TestCase):
    def test_a(self):
        self.assertTextFilesEqual("file_a.txt", "file_b.txt")

This mixin can be useful for any situation where you are comparing files during tests.

The directory and archive comparison functions have optional parameters that allow one archive/directory to be a superset of the other and still pass. This does not propagate recursively.

IsolatedWorkingDirMixin

This class causes each test to execute in an isolated temporary directory. It also provides options to connect external files via ExternalConnection. Default connection method is "symlink", with "copy" also available in addition to providing a callable that takes the absolute source path and the destination path relative to the test directory. This callable could do something like copy and use the example config files if none were provided.

class MyIsolatedTestCase(IsolatedWorkingDirMixin, unittest.TestCase):
    external_connections = [IsolatedWorkingDirMixin.ExternalConnection(external_path="utils/")]

    def test_a(self):
        data = pd.read_csv("utils/data.csv")

This mixin can be useful when you are testing something that modifies its working directory.

ScenarioTestCaseMixin

In the following example each file will be opened and converted to uppercase. For each scenario the final_state will presumably contain all the files from initial_state in upper case.

class MyScenarioTestCase(ScenarioTestCaseMixin, unittest.TestCase):
    scenarios_dir = Path(__file__).parent / "scenarios"

    def run_scenario(self, scenario_name: str, scenario_path: str) -> None:
        for filename in os.listdir():
            with open(filename, "r") as f:
                content = f.read()
            with open(filename, "w") as f:
                f.write(content.upper())

In a more realistic or topical example:

class MyScenariosTestCase(ScenarioTestCaseMixin, unittest.TestCase):
    scenarios_dir = Path(__file__).parent / "successful_scenarios"
    check_strategy = OutputChecking.FILE_CONTENTS
    external_connections = [
        ExternalConnection(external_path=Path(__file__).parent.parent / "utils", strategy="symlink"),
        ExternalConnection(external_path=Path(__file__).parent.parent / "config", strategy=copy_config),
        ExternalConnection(external_path=Path(__file__).parent.parent / "workflow", strategy="symlink"),
    ]
    match_final_state_exactly = False

    def run_scenario(self, scenario_name: str, scenario_path: str) -> None:
        expected_outputs = list(os.listdir(os.path.join(scenario_path, "final_state")))
        result = subprocess.run(["snakemake", "--use-conda"] + expected_outputs)
        self.assertEqual(0, result.returncode, f"Snakemake had non-zero return code: {result.returncode}")

This example sets the directory for scenarios, sets the checking strategy to recursively compare the contents of the files produced, connects external resources to the working directory, including config files with custom handling, and allows extra items in the working directory output that are not present in the expected final state, to allow for intermediate files that aren't cleaned up and so on. The run function gathers the expected outputs from the final state and requests them from snakemake, checking that snakemake exists normally.

Metadata

Release files for unittest-scenarios 1.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for unittest-scenarios 1.4.0
File Size Uploaded Provenance Actions
unittest_scenarios-1.4.0.tar.gz 10.1 kB Without provenance Details for unittest_scenarios-1.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for unittest-scenarios 1.4.0
File Interpreter ABI Platform Actions
unittest_scenarios-1.4.0-py3-none-any.whl Python 3 none any Details for unittest_scenarios-1.4.0-py3-none-any.whl

Total release size: 21.2 kB

Release files / unittest_scenarios-1.4.0.tar.gz

Download URL unittest_scenarios-1.4.0.tar.gz
Size 10.1 kB
Tags Source
SHA-256 checksum
How to use checksums
8d730ff14830363e93d4ce2336ffb662ec0d6c1f2c7ebc0776c552d4e74f760b
BLAKE2b-256 checksum
How to use checksums
1a224c0f6869fcabc95b81b227b995ae55f10a147b8ebe198cd1ebdfc5d50842
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.1.1 CPython/3.13.1 Linux/6.8.0-1021-azure

Release files / unittest_scenarios-1.4.0-py3-none-any.whl

Download URL unittest_scenarios-1.4.0-py3-none-any.whl
Size 11.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2c101b85d06e7e6bdecddc643c97f2354462a0a31f7521298787245318a73443
BLAKE2b-256 checksum
How to use checksums
fe422eba7fed5a9a6c831b762bc249a756eb205756a6342037bf8a7b31bcee4d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.1.1 CPython/3.13.1 Linux/6.8.0-1021-azure

Security for unittest-scenarios 1.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page