Search code examples
pythonpytestmoto

What is the proper way to use mock_secretsmanager in a module scoped pytest fixture?


This does not work

@pytest.fixture(scope="module")
def monkeypatch_module():
    # gross bug: https://github.com/pytest-dev/pytest/issues/363
    from _pytest.monkeypatch import MonkeyPatch

    mpatch = MonkeyPatch()
    yield mpatch
    mpatch.undo()

@pytest.fixture(scope="module")
@mock_secretsmanager
def setup_stuff(monkeypatch_module):
    secret_name = "test_mock_secret01"
    sm_client = boto3.client("secretsmanager", region_name="us-east-1")
    sm_client.create_secret(
        Name=secret_name,
        SecretString='{"username":"mockuser","password":"mockpass"}',
    )
    # module level env vars
    monkeypatch_module.setenv("MY_VAR", "sldkfjsdf")

@pytest.mark.unittest
def test__mytest(setup_stuff):
    secret_name = "test_mock_secret01"
    my_method_that_gets_the_secret(secret_name)

I get this error:

botocore.errorfactory.ResourceNotFoundException: An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets Manager can't find the specified secret.

I had to make it a function and use it like this:

@mock_secretsmanager
def setup_stuff(monkeypatch_module):
    secret_name = "test_mock_secret01"
    sm_client = boto3.client("secretsmanager", region_name="us-east-1")
    sm_client.create_secret(
        Name=secret_name,
        SecretString='{"username":"mockuser","password":"mockpass"}',
    )
    # module level env vars
    monkeypatch_module.setenv("MY_VAR", "sldkfjsdf")


@mock_secretsmanager
@pytest.mark.unittest
def test__mytest(monkeypatch, monkeypatch_module):
    setup_stuff(monkeypatch_module)

    # function level env vars
    monkeypatch.setenv("MY_LOCAL_VAR", "sldkfjsdf")

But this will run with every function call.

I just want to create a fixture that creates mock secrets (sets env vars and other stuff) once for the entire module.

What is the proper way to use mock_secretsmanager in a module scoped fixture?


Solution

  • Moto resets its internal state, every time a mock starts and ends. This is necessary to ensure that state doesn't leak in between tests.

    Adding mock_secretsmanager to setup_stuff ensures that these secrets exist for the duration of that particular method - but the moment the method/mock ends, it will delete the state again.

    Your solution, to yield a mock and pass that to a method, ensures that the with mock_secretsmanager() context is started at the beginning of the test, and active for the entire duration. That's why it works.

    Note that this is indeed the recommended approach when using fixtures: see http://docs.getmoto.org/en/latest/docs/getting_started.html#recommended-usage