2

I am trying to mock secret manager. Here is the code which is written for secret manager using boto3 which I am trying to mock and test.

utils.py

import boto3

secret_id = os.environ.get("SECRETS") 
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId=secret_id)
secrets = json.loads(response['SecretString'])
S3_BUCKET_NAME = secrets["S3_BUCKET_NAME"]
SQS_QUEUE_NAME = secrets["SQS_Queue_Name"]

these variables are then used in different methods.

conftest.py

@pytest.fixture(scope='session', autouse=True)
def secret_manager_resource(aws_credentials):
    """Secret Manager mock client"""
    with mock_secretsmanager():
        conn = boto3.client("secretsmanager", region_name="us-east-1")
        logger.info(f"Secret manager connection {conn}")
        yield conn

test_file.py

@contextmanager
def secret_manager_setup(secret_manager_resource):

    secret_manager_resource.create_secret(Name="test", SecretString="""{"S3_BUCKET_NAME": "test","SQS_Queue_Name": "test_queue"}""")
    yield

class TestSecretManager:
    def test_secret_manager(self, secret_manager_resource):
        with secret_manager_setup(secret_manager_resource):
            try:
                result = secret_manager_resource.get_secret_value(SecretId="test")
                json_result = json.loads(result['SecretString'])
                assert json_result["S3_BUCKET_NAME"] == "test"
                assert json_result["SQS_Queue_Name"] == "test_queue"
            except Exception as err:
                print("Error ---", err)

class TestClass:
   def test_some_class(test_var):
     from functions.something.some import something
     something = someClass({}, param)

When I run pytest it directly goes inside TestClass and calls secret Manager and throws error as it is trying to connect to actual secret manager. Could someone suggest me what can be done to over come this issue?

Lovika
  • 577
  • 2
  • 10
  • 21

1 Answers1

0

TestClass is not mocked - so I wouldn't expect that to work. You could use Moto as a class-decorator to ensure everything inside someClass is mocked.

Note that the class-decorator creates a mock around test-methods only, so the code-under-test would have to be inside a test-method for this to work.

@mock_secretsmanager()
class TestClass:

    def test_something():
        from functions.something.some import something
        something = someClass({}, param)

See http://docs.getmoto.org/en/latest/docs/getting_started.html#class-decorator for the documentation and more examples around this.

Bert Blommers
  • 1,788
  • 2
  • 13
  • 19
  • Still getting the error for secret manager as it's trying to connect to aws even after making this change `@mock_secretsmanager() class SomeClass: logger.info("Starting!!!!!") # os.environ.set("SECRETS", "test") from functions.process_rules.file import Sample sampl = Sample()` – Lovika Apr 26 '22 at 08:20
  • I've updated my answer to be more explicit @Lovika – Bert Blommers Apr 26 '22 at 09:47
  • I updated the code but the function is throwing parameter error, I passed random variable just to test if the test-method is working but it's still trying to reach aws secret manager and throwing this error `"botocore.errorfactory.ResourceNotFoundException: An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets Manager can't find the specified secret."` – Lovika Apr 26 '22 at 10:12
  • Moto will throw that exception as well. A secret would have to be created first, before it can be read - Moto follows the exact behaviour as AWS here – Bert Blommers Apr 26 '22 at 10:32
  • Please check the question, I am already creating the secret in method secret_manager_setup and when I remove the TestClass, the secret is getting created and test method test_secret_manager also runs and passes. The issue is occuring only inside TestClass and when TestClass and other test Classes are defined. – Lovika Apr 26 '22 at 10:35
  • The question does not show that the `secret_manager_setup` is called from within TestClass. Each test-method has it's own state, to avoid leakage in between tests. The secret is explicitly created in `TestSecretManager`, but the moment you execute `TestClass`, it starts with a clean slate - and the secret will not exist until you create it again. See http://docs.getmoto.org/en/latest/docs/getting_started.html#class-decorator – Bert Blommers Apr 26 '22 at 10:53
  • oh, I can't pass secret_manager_setup to TestClass as secrets value variable is a global variable and not being passed to any method. Is there any workaround for it? – Lovika Apr 26 '22 at 11:36
  • That's difficult to say without knowing the full setup. Can you not reuse the same fixture, like `TestClass.test_something(secret_manager_setup)`? – Bert Blommers Apr 26 '22 at 14:27
  • Not sure if `TestClass.test_something(secret_manager_setup)` will work as there is this piece of code which is common to all the test methods `from functions.process_rules.file import Sample sampl = Sample()` and how do I reuse the same fixture in other test methods when the secret manager value is a global variable not a method parameter. – Lovika Apr 26 '22 at 14:39