This is a AWS lambda function
#service.py
from configs import SENDER, DESTINATIONS
from constants import LOG_FORMAT
import logging
def send_mail(body, SENDER, DESTINATIONS):
...
...
In config files it's retrieving data from AWS param store
# configs.py
from handlers.ssm_handler import load_parameters
from common import constants
import os
environment = os.environ.get(constants.ENVIRONMENT)
JSON_BUCKET = load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MIGRATION_BUCKET)
SENDER = load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_SENDER)
DESTINATIONS = load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_DESTINATIONS)
...
So when i try to test it
# test_service.py
from unittest import TestCase, main, mock
from service import send_mail
class TestMailService(TestCase):
def test_service(self):
with mock.patch('service.SENDER', '[email protected]') as mocked_sender:
with mock.patch('service.DESTINATIONS', '[email protected]') as mocked_sender:
with mock.patch('service.logging.Logger.info') as mocked_logging:
send_mail(...)
mocked_logging.assert_called_with('mail sent Successfully')
This test case is passing when I export AWS Security credentials. But it'wont, if i don't pass the credentials. I guess it's because in the service.py file it's opening the entire config.py file. So it would require sec credentials to call AWS. As a solution I tried mocking SENDER and DESTINATIONS. But it throws me error(expecting security tokens)
I want the unittest to be security token independent. Suggest a solution
CodePudding user response:
It happens because when you import configs.py e.g. via from configs import SENDER, DESTINATION
, it would automatically run those statements that call load_parameters
which in turn calls AWS SSM even if there are no active mocks/patches yet.
Solution 1
Try refactoring configs.py in a way that the setting of the variables would only happen upon an explicit call (and not upon import). The simplest implementation would be something like:
configs.py
import os
from common import constants
from handlers.ssm_handler import load_parameters
def get_params():
environment = os.environ.get(constants.ENVIRONMENT)
return {
"SENDER": load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_SENDER),
"DESTINATIONS": load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_DESTINATIONS),
}
This would need some refactoring as the call to get_params
must be inserted at the start of the AWS Lambda function call. This way, the calling of load_parameters
which in turn uses AWS SSM would not be executed automatically and we can prepare our mocks/patches before it is called.
Solution 2
Don't import any file that would in turn import configs.py while there is no active mock/patch yet. Patch load_parameters
first so that it doesn't connect to the actual AWS SSM. You can patch it manually or you can use the decorator @mock_ssm
from moto. Only then we can safely import the files.
from unittest import TestCase, main, mock
from moto import mock_ssm
# from service import send_mail # REMOVE THIS IMPORT!
@mock_ssm # Option 1. Requires <pip install moto>. You have to setup SSM first as usual.
def test_service(mocker): # Requires <pip install pytest-mock>
mocker.patch('handlers.ssm_handler.load_parameters') # Option 2
# with mock.patch('handlers.ssm_handler.load_parameters') as mock_ssm: # Option 3. This is equivalent to Option 2.
mocker.patch('service.SENDER', '[email protected]')
mocker.patch('service.DESTINATIONS', '[email protected]')
from service import send_mail # Import it here after the patches have taken effect
send_mail(...)