How do I correctly write a unit test for a function that uses the GCP secret manager client library. I've been reading up on unit testing and mocking but I just can't seem to grasp what's going wrong here. I've never really written unit tests other than very basic ones, or done mocking either. I have the following get_secret function in a file main.py that returns a string.
from google.cloud import secretmanager
def get_secret(project_id,secret_name) -> str:
"""
Get secret from gcp secrets manager
"""
client = secretmanager.SecretManagerServiceClient()
request = {"name": f"projects/{project_id}/secrets/{secret_name}/versions/latest"}
response = client.access_secret_version(request)
secret_string = response.payload.data.decode("UTF-8")
return secret_string
I have the following test_main.py file where I try to mock the secretmanager.
import pytest
from unittest.mock import patch
from main import get_secret
@pytest.fixture()
def secret_string():
return 'super_secret_token'
@patch("main.secretmanager") # mock secretmanager from main.py
def test_get_secret(secretmanager,secret_string):
secretmanager.SecretManagerServiceClient().access_secret_version().return_value = secret_string
secret_string = get_secret('project_id','secret_name')
assert secret_string == 'super_secret_token'
When I run pytest it test fails with AssertionError: assert <MagicMock name='secretmanager.SecretManagerServiceClient().access_secret_version().payload.data.decode()' id='4409262192'> == 'super_secret_token'
I have an idea why but I'm not entirely sure. I assume it's to do with access_secret_version() returning an object of type google.cloud.secretmanager_v1.types.service.AccessSecretVersionResponse which has a payload object of type google.cloud.secretmanager_v1.types.SecretPayload which is a data object of type bytes
Any help on how to do this correctly would be appreciated.
@patch("main.secretmanager") attempts to patch the secretmanager which is a module but I need to be patching secretmanager.SecretManagerServiceClient which is a class, I had tried this but it was giving me errors due to the incorrect syntax I was using for return_value. It's now working with.
@patch("main.secretmanager.SecretManagerServiceClient")
def test_get_secret(self, mock_smc):
mock_smc.return_value.access_secret_version.return_value.payload.data = b'super_secret_token'
secret_string = get_secret('project_id', 'secret_name')
assert secret_string == 'super_secret_token'
You could also patch access_secret_version() but when running the test it will attempt to authenticate with gcp when client = secretmanager.SecretManagerServiceClient() is called and if there are no valid credentials it will fail. It's probably best not to authenticate with external service when running unit tests.
from unittest.mock import patch
from main import get_secret
@patch("main.secretmanager.SecretManagerServiceClient.access_secret_version")
def test_get_secret(secretmanager):
secretmanager.return_value.payload.data = b'super_secret_token'
secret_string = get_secret('project_id', 'secret_name')
assert secret_string == 'super_secret_token'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With