Disable autouse fixtures on specific pytest marks

Question:

Is it possible to prevent the execution of “function scoped” fixtures with autouse=True on specific marks only?

I have the following fixture set to autouse so that all outgoing requests are automatically mocked out:

@pytest.fixture(autouse=True)
def no_requests(monkeypatch):
    monkeypatch.setattr("requests.sessions.Session.request", MagicMock())

But I have a mark called endtoend that I use to define a series of tests that are allowed to make external requests for more robust end to end testing. I would like to inject no_requests in all tests (the vast majority), but not in tests like the following:

@pytest.mark.endtoend
def test_api_returns_ok():
    assert make_request().status_code == 200

Is this possible?

Asked By: Mattew Whitt

||

Answers:

I wasn’t able to find a way to disable fixtures with autouse=True, but I did find a way to revert the changes made in my no_requests fixture. monkeypatch has a method undo that reverts all patches made on the stack, so I was able to call it in my endtoend tests like so:

@pytest.mark.endtoend
def test_api_returns_ok(monkeypatch):
    monkeypatch.undo()
    assert make_request().status_code == 200
Answered By: Mattew Whitt

It would be difficult and probably not possible to cancel or change the autouse

You can’t canel an autouse, being as it’s autouse. Maybe you could do something to change the autouse fixture based on a mark’s condition. But this would be hackish and difficult.

possibly with:

import pytest
from _pytest.mark import MarkInfo

I couldn’t find a way to do this, but maybe the @pytest.fixture(autouse=True) could get the MarkInfo and if it came back ‘endtoend’ the fixture wouldn’t set the attribute. But you would also have to set a condition in the fixture parameters.

i.e.: @pytest.fixture(True=MarkInfo, autouse=True). Something like that. But I couldn’t find a way.


It’s recommended that you organize tests to prevent this

You could just separate the no_requests from the endtoend tests by either:

  1. limit the scope of your autouse fixture
  2. put the no_requests into a class
  3. Not make it an auto use, just pass it into the params of each def you need it

Like so:

class NoRequests:
    @pytest.fixture(scope='module', autouse=True)
    def no_requests(monkeypatch):
        monkeypatch.setattr("requests.sessions.Session.request", MagicMock())
    def test_no_request1(self):
        # do stuff here
    # and so on

This is good practice. Maybe a different organization could help


But in your case, it’s probably easiest to monkeypatch.undo()

Answered By: Comradsky

You can also use the request object in your fixture to check the markers used on the test, and don’t do anything if a specific marker is set:

import pytest

@pytest.fixture(autouse=True)
def autofixt(request):
    if 'noautofixt' in request.keywords:
        return
    print("patching stuff")

def test1():
    pass

@pytest.mark.noautofixt
def test2():
    pass

Output with -vs:

x.py::test1 patching stuff
PASSED
x.py::test2 PASSED
Answered By: The Compiler

In case you have your endtoend tests in specific modules or classes you could also just override the no_requests fixture locally, for example assuming you group all your integration tests in a file called end_to_end.py:

# test_end_to_end.py

@pytest.fixture(autouse=True)
def no_requests():
    return

def test_api_returns_ok():
    # Should make a real request.
    assert make_request().status_code == 200

Answered By: Jarno
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.