When writing code it is always a good idea to write testable code or easy to test code. However, sometimes you need to test code, which is not as easy to test as wanted. A useful trick that can assist you while testing code is monkeypatching global variables.
Suppose you have the following script.py:
from pathlib import Path
data_directory = Path(__file__).parent / "data"
print(data_directory)
The data_directory
may be used for finding data resources or saving new files.
However, when testing this code, you do not want to modify the documents in the data directory.
Instead, a temporary directory should be used, where additional temporary files can be created.
To do so, the data_directory
variable needs to be overwritten in tests.
Fortunately, we can utilise pytest's monkeypatch fixture:
# test.py
import script
def test_overwrite_data_dir(tmp_path, monkeypatch) -> None:
data_directory = tmp_path / "data"
monkeypatch.setattr(script, "data_directory", data_directory)
assert data_directory == script.data_directory
Pytest provides a context manager, which can be used to test that a certain piece of code raises an exception.
In order to test different input values and whether or not an exception is raised, pytest's parametrisation feature can be combined with nullcontext
from the contextlib module.
from contextlib import nullcontext as does_not_raise
import pytest
@pytest.mark.parametrize(
"example_input,expectation",
[
(3, does_not_raise()),
(2, does_not_raise()),
(1, does_not_raise()),
(0, pytest.raises(ZeroDivisionError)),
],
)
def test_division(example_input, expectation):
"""Test how much I know division."""
with expectation:
assert (6 / example_input) is not None
Reference: https://stackoverflow.com/a/68012715/6707020
Pytest provides you with the capability to create custom markers in order to select or deselect tests more gradually.
Suppose you want to mark all command-line interface-related tests with a custom cli
marker.
To do so, you need to register the marker first in your pytest.ini as follows:
[pytest]
markers =
cli: mark a test as cli-related.
Now, you can use the marker cli
to mark all command-line interface related tests with it as follows:
import pytest
@pytest.mark.cli
def test_some_cli_test():
pass
Selecting marked tests is as easy as running:
$ pytest -m cli
... or deselecting them by using pytest's "not"-keyword:
$ pytest -m "not cli"
Reference: Marking test functions and selecting them for a run