0

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?

red888
  • 27,709
  • 55
  • 204
  • 392

2 Answers2

1

So this seems to work, but I would still like to understand why. Do the decorators just not work with fixtures?

@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")
def mocked_sm_client():
    """Secret Manager mock client"""
    with mock_secretsmanager():
        conn = boto3.client("secretsmanager", region_name="us-east-1")
        yield conn


@pytest.fixture(scope="module")
def setup_stuff(monkeypatch_module, mocked_sm_client):
    secret_name = "test_mock_secret01"
    mocked_sm_client.create_secret(
        Name=secret_name,
        SecretString='{"username":"mockuser1324","password":"mockpass"}',
    )
    # module level env vars
    monkeypatch_module.setenv("MY_VAR", "sldkfjsdf")


@pytest.mark.unittest
def test__mytest(setup_stuff, monkeypatch):
    # fucntion level env vars
    monkeypatch.setenv("MY_LOCAL_VAR", "sldkfjsdf")

    secret_name = "test_mock_secret01"
    # Now it finds the mocked secret object
    my_method_that_gets_the_secret(secret_name)
red888
  • 27,709
  • 55
  • 204
  • 392
1

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

halfer
  • 19,824
  • 17
  • 99
  • 186
Bert Blommers
  • 1,788
  • 2
  • 13
  • 19