# How to Disable Autouse Fixtures in pytest

`pytest` is a very robust framework that comes with lots of features. 

One such feature is the `autouse` fixtures, a.k.a xUnit setup on steroids. They are a special type of fixture that gets invoked automatically, and its main use case is to act as a setup/teardown function. 

Another use case is to perform some task, like mocking an external dependency, that must happen before every test.

For example, suppose you have a set of functions that execute HTTP calls. For each one, you provide a test. To ensure your test doesn't call the real API, we can [mock the call](https://miguendes.me/3-ways-to-test-api-client-applications-in-python) using a library such `responses`. 

However, if you want one of the tests to call the API, as in an integration test, then you'll have to disable the `autouse` fixture. And that's what we're going to see today.

In this post, we'll learn a simple technique to disable `autouse` fixtures for one or more tests.

## Table of Contents

1. [`pytest`'s `autouse` fixture - example](#pytests-autouse-fixture-example)
2. [Disabling an `autouse` fixture ](#disabling-an-autouse-fixture)
3. [Conclusion](#conclusion)

## `pytest` Fixture Autouse - Example

In this section, we'll build an example to illustrate the usage of [autouse fixtures](https://docs.pytest.org/en/6.2.x/fixture.html#autouse-fixtures-fixtures-you-don-t-have-to-request) and how to we can disable them when necessary. 

For this example, we'll write some tests that mock the random module. 

Consider the following case where we'll be building a random password generator. The function takes a password length and returns a random string of size *length*. And to do that, it uses `random.choices` to randomly pick `k` chars from a seed string called `all_chars`.

```python
# file: autouse/__init__.py

import random
import string


def get_random_password(length: int = 20) -> str:
    """
    Generates a random password with up to length chars.
    """

    all_chars = string.ascii_letters + string.digits + string.punctuation
    return ''.join(random.choices(all_chars, k=length))
```

Since we don't control how `random.choices` picks, we cannot test it in a deterministic way. To make that happen, we can patch `random.choices` and make it return a fixed list of chars.

> You can also set the `random.seed` to a fixed number before every test run by making it an *autouse fixture*.

```python
# file: tests/test_random.py

import random

import pytest

from autouse import get_random_password


@pytest.fixture(autouse=True)
def patch_random():
    with unittest.mock.patch('autouse.random.choices') as mocked_choices:
        mocked_choices.return_value = ['a', 'B', 'c', '2']
        yield


def test_mocked_random_char():
    assert get_random_password() == 'aBc2'
```

The benefits of the `autouse` fixture are that we don't need to pass it to every test that needs it. And by using `yield`, you undo the patching after the test finishes, which is great for cleaning up.

If we run this test, it passes just fine.

```console
============================= test session starts ==============================
collecting ... collected 1 item

test_random.py::test_mocked_random_char PASSED                           [100%]

========================= 1 passed, 1 warning in 0.05s =========================
```

![pytest autouse fixture being injected inside a test](https://cdn.hashnode.com/res/hashnode/image/upload/v1632589051784/ikRPvi5MW.png)

## Disabling an `autouse` fixture 

Now, let's say that we want to test the robustness of our random number generator and we want to test that it never generates the same string in a row.

To do that, we need to call the real function, and not patch it. Let's create this test and see what it does.

```python
# file: tests/test_random.py

def test_random_char_does_not_duplicate():
    password_one = get_random_password()
    password_two = get_random_password()
    assert password_one != password_two
```

But when we run this test, it fails:

```console
test_random.py::test_random_char_does_not_duplicate FAILED               [100%]
test_random.py:18 (test_random_char_does_not_duplicate)
def test_random_char_does_not_duplicate():
        password_one = get_random_password()
        password_two = get_random_password()
>       assert password_one != password_two
E       AssertionError: assert 'aBc2' != 'aBc2'

test_random.py:22: AssertionError
```
The reason is that `pytest` injects the `autouse` fixture to every test case within the scope you specified.

Now the question is, how can we disable an `autouse` fixture for one or more tests in pytest?

One way to do that is to create a custom `pytest` mark and annotate the test with it. For example:

```python
@pytest.fixture(autouse=True)
def patch_random(request):
    if 'disable_autouse' in request.keywords:
        yield 
    else:
        with unittest.mock.patch('autouse.random.choices') as mocked_choices:
            mocked_choices.return_value = ['a', 'B', 'c', '2']
            yield
```
In this example, we created a `pytest` mark called `disable_autouse` and we annotated the `test_random_char_does_not_duplicate` test with it. 

This mark becomes available in the [request fixture](https://docs.pytest.org/en/6.2.x/reference.html?highlight=request#request). We can pass this `request` fixture to the `autouse` one and check if the keyword `disable_autouse` is in the list of keywords.

When that's the case, we don't mock, just `yield`, which gives back the control to `test_random_char_does_not_duplicate`, thus avoiding mocking the `random.choices` function.

Let's see what happens when we run the test with this mark...

```python
@pytest.mark.disable_autouse
def test_random_char_does_not_duplicate():
    password_one = get_random_password()
    password_two = get_random_password()
    assert password_one != password_two
```
The test passes, since it's not mocked anymore.

```console
============================= test session starts ==============================
collecting ... collected 1 item

test_random.py::test_random_char_does_not_duplicate PASSED               [100%]

========================= 1 passed, 1 warning in 0.03s =========================
```

## Conclusion

`pytest` has some great features such as `autouse` fixture. They make it easier to set up and teardown unit tests but if we ever want to disable it, then things get trickier.

In this post, we learned how to disable autouse fixture in pytest by marking the tests with a [custom pytest mark](https://docs.pytest.org/en/6.2.x/mark.html). I hope you enjoyed this article and see you next time.

Other posts you may like:

- [Learn how to unit test REST APIs in Python with Pytest by example.](https://miguendes.me/3-ways-to-test-api-client-applications-in-python)

- [7 pytest Features and Plugins That Will Save You Tons of Time](https://miguendes.me/7-pytest-features-and-plugins-that-will-save-you-tons-of-time)

- [How to Use Fixtures as Arguments in pytest.mark.parametrize](https://miguendes.me/how-to-use-fixtures-as-arguments-in-pytestmarkparametrize)

- [How to Check if an Exception Is Raised (or Not) With pytest](https://miguendes.me/how-to-check-if-an-exception-is-raised-or-not-with-pytest)

- [7 pytest Plugins You Must Definitely Use](https://miguendes.me/7-pytest-plugins-you-must-definitely-use)

- [How to Unit Test Complex Data Like Numpy Arrays in Python](https://miguendes.me/how-to-test-complex-data-in-python)


References:

[Disable autouse fixtures on specific pytest marks](https://stackoverflow.com/questions/38748257/disable-autouse-fixtures-on-specific-pytest-marks)

[pytest - is there a way to ignore an autouse fixture?](https://stackoverflow.com/questions/39558812/pytest-is-there-a-way-to-ignore-an-autouse-fixture)

This article was originally published at [https://miguendes.me](https://miguendes.me/pytest-disable-autouse)

