36

I use the following to mock constant values for a test with py.test:

@patch('ConstantsModule.ConstantsClass.DELAY_TIME', 10)
def test_PowerUp():
    ...
    thing = Thing.Thing()
    assert thing.a == 1

This mocks DELAY_TIME as used in both the test, and in Thing, which is what I expected.

I wanted to do this for all the tests in this file, so I tried

@patch('ConstantsModule.ConstantsClass.DELAY_TIME', 10)
@pytest.fixture(autouse=True)
def NoDelay():
    pass

But that doesn't seem to have the same effect.

Here is a similar question: pytest-mock mocker in pytest fixture, but the mock seems done in a non-decorator way there.

sg_man
  • 763
  • 1
  • 6
  • 14

2 Answers2

63

I'd say patching via decorator is not the optimal approach here. I'd use the context manager:

import pytest
from unittest.mock import patch


@pytest.fixture(autouse=True)
def no_delay():
    with patch('ConstantsModule.ConstantsClass.DELAY_TIME', 10):
        yield

This way, patching is cleanly reverted on test teardown.

hoefling
  • 59,418
  • 12
  • 147
  • 194
15

pytest provides builtin patching support via the monkeypatch fixture. So to patch the constant for all tests in the file you can create the following autouse fixture:

@pytest.fixture(autouse=True)
def no_delay(monkeypatch):
    monkeypatch.setattr(ConstantsModule.ConstantsClass, 'DELAY_TIME', 10)

If you don't want to have the ConstantsModule imported in your tests you can use a string, see the full API reference.

flub
  • 5,953
  • 27
  • 24
  • 3
    Beware that `monkeypatch` is a function-scoped fixture, so your test will raise a `ScopeMismatch`. – hoefling Apr 26 '18 at 19:57
  • 3
    Whoops, that'll teach me for not testing the code I post. Thanks for the heads up, I've corrected it. – flub Apr 27 '18 at 19:56