additional notes on python testing

This commit is contained in:
Thomas Bishop 2026-01-08 19:44:37 +00:00
parent 52f4420b73
commit 5d37a1efb2
2 changed files with 94 additions and 8 deletions

88
zk/Pytest_fixtures.md Normal file
View file

@ -0,0 +1,88 @@
---
tags: [python, pytest]
---
Fixtures are a way to provide data or state to your tests. Typically, although
not exclusively, for processes that you want to reuse accross different `test_`
functions. They are defined at the "arrange" stage of testing.
## Example: testing class methods under test
```py
class Person:
def __init__(self, name):
self.name = name
def print_name(self):
return f"Your name is {self.name}"
```
To create a fixture of this class:
```py
@pytest.fixture
def my_person():
person = new Person('Thomas')
return person.name()
test_print_name(my_person):
assert(my_person) == 'Thomas'
```
> Note we define our fixture and then inject it into our test function.
> Functions request fixtures they require by declaring them as arguments.
Alternatively you could just return the class from the fixture and test the
methods individually within the test function.
## Integrating with `@patch` to provide mock fixtures
Say we wanted to mock reading a JSON file.
We could create a fixture:
```py
@pytest fixture
def raw_json():
return json.dumps({"key": "value"})
```
And pass it in to the mock of the global `open` method:
```py
from unittest.mock import mock_open, patch
def test_json_parsing(raw_json):
with patch("builtins.open", mock_open(read_data=raw_json))
# Then assert etc
```
This fixture could then be re-used to test any other method that relies on
incoming JSON from a file.
## Example: setup and teardown - before each and after each
Fixtures can be leveraged to setup 'before each...' and 'after each...' logic:
```py
@pytest.fixture(scope="function")
def setup():
os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"
yield
del os.environ["POCKET_LAMBDA_ENDPOINT"]
```
Here `yield` is a placeholder for the tests that run in between the setup and
teardown.
We use `scope="function"` to signal that this will run before/after any tests
that inject the fixture.
Then to specify the units that will run as the `yield` we just inject `setup`:
```py
def some_function(setup_function):
pass
```

View file

@ -1,18 +1,16 @@
--- ---
tags: [python, testing] tags: [python, testing, pytest]
--- ---
# Testing Python code
## `pytest` ## `pytest`
Pytest is the most popular testing library for Python. It is not included with Pytest is the most popular testing library for Python. It is not included with
the Python standard library so it must be installed with the Python standard library so it must be installed with
[pip](Python_package_management.md). [pip](Python_package_management.md). While it does not include a declaration
While it does not include a declaration library, it is robust enough to handle library, it is robust enough to handle most scenarios having a rich and
most scenarios having a rich and expressive set of constructs and decorators expressive set of constructs and decorators that let you declare what your tests
that let you declare what your tests should do, under what conditions they should do, under what conditions they should run, and how they should interact
should run, and how they should interact with the rest of your system. with the rest of your system.
### Using `pytest` ### Using `pytest`