Search code examples
pythonpytestpython-unittestmoto

pytest patch return type of a method call


I want to patch a method return of AWS secrets manager. I am creating a secret and then i am invoking rotate_secret. So once the rotation happens the list_secret call should return a JSON object with a key NextRotationDate. Since moto is currently not returning the key my tests are failing.

Here is the code i want to test -

def list(self):
    client: SecretsManagerClient = boto3.client("secretsmanager")
    secrets_list = client.list_secrets(
        Filters=[
            {"Key": "name", "Values": ["test"]},
        ]
    )["SecretList"]

    return secrets_list

test.py ->

def test_list(self):
    secret_client = boto3.client("secretsmanager")
    secret_client.create_secret(
        Name="test-secret",
        Tags=[
            {"Key": "id", "Value": "xyz"}
        ],
    )

    secret_client.rotate_secret(
        SecretId="test-secret",
        RotationRules={'AutomaticallyAfterDays': 30},
        RotationLambdaARN=mock_rotation_lambda,
        RotateImmediately=True,
    )

    result = secret_client.list_secrets()
    # I add the NextRotationDate manually here to have the correct result
    result["SecretList"][0]['NextRotationDate'] = datetime(2024, 1, 1)

    with patch.object(secret_client, "list_secrets", return_value=result):
        result = sut.list()

Somehow the list_secrets is always returning the secret without the needed key. How can i patch it in a way that the list_secrets in the test_list return the result i am passing in the patch?


Solution

  • You can patch botocore to intercept the request, and enrich the response with that particular field.

    import boto3
    import botocore
    from unittest.mock import patch
    
    from moto import mock_ec2
    
    
    orig = botocore.client.BaseClient._make_api_call
    
    def mock_make_api_call(self, operation_name, kwarg):
        if operation_name == 'ListSecrets':
            print("Intercepting... ")
            resp = orig(self, operation_name, kwarg)
            # Change the response as appropriate 
            ...
            return resp
        return orig(self, operation_name, kwarg)
    
    
    def test_list():
        ...
    
        with patch('botocore.client.BaseClient._make_api_call', new=mock_make_api_call):
            result = sut.test_list()
    

    See the documentation: https://docs.getmoto.org/en/latest/docs/services/patching_other_services.html