up:: [[Python MOC]]
# Testing Python Code with Pytest
## Mocking External Code
To mock outside function calls with Pytest, install the `pytest-mock` package and use `mocker` as a parameter to your test function. Then you set the patch with the following syntax: `mocker.patch.object(Class, "function_name", replacement_function)`. Note how there aren't any parantheses for the functions - either in the string representation or the actual function. This is because if they were used, the function would be evaluated at the call, rather than passed in to the patch.object function. These are best done in setup functions rather than the test function itself.
**Note:** The `mocker` object and `pytest-mock` modules don't need to be separately imported.
### Example
```python
@pytest.fixture(params=[1, 2])
def generate_test_parameters(request, mocker):
mocker.patch.object(FeatureModel, "query", query_mock)
def query_mock(*args, **kwargs):
do_something()
```
## Using Fixtures for Setup and Teardown
Fixtures should be used for setup and teardown functionality, including setting up mocks. These are functions decorated with `@pytest.fixture()` and if you need to re-run a fixture with multiple parameters you give the decorator a `params` parameter. This can then be accessed via the `request` object passed into the fixture function, and the fixture function is itself passed into the appropriate test function(s).
### Example
```python
@pytest.fixture(params=[1, 2])
def generate_test_parameters(request):
if request.param == 1:
do_something()
return test_params, expected_output
def test_function(generate_test_parameters):
out = function_under_test(generate_test_parameters[0])
assert out == generate_test_parameters[1]
```
## Methods for Adding Teardowns to Fixtures
There are two ways to add teardown functionality to Pytest fixtures:
1. Change the `return` statement to a `yield` one and put all your teardown code after that.
2. Use the `request` object and call `.addfinalizer()` with the teardown function passed in as a parameter.
### Example
```python
# yield technique
@pytest.fixture
def receiving_user(mail_admin):
user = mail_admin.create_user()
yield user
mail_admin.delete_user(user)
# addfinalizer() technique
@pytest.fixture
def receiving_user(mail_admin, request):
user = mail_admin.create_user()
def delete_user():
mail_admin.delete_user(user)
request.addfinalizer(delete_user)
return user
```
### Source
[Pytest docs](https://docs.pytest.org/en/6.2.x/fixture.html#yield-fixtures-recommended)