need help on how to re create the same environment in moto so my python tests work

205 Views Asked by At

I need help on how to re create the same environment in moto so my python tests work

The code is writen in python and tests were implemented using unittest, boto3, and moto.

2 files are contained in the same directory called

Main file being tested (lambda_function.py) Test File (test_api_login.py)

See the errors output

/usr/bin/python3 "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --target test_api_login.py::test_login_to_api
Testing started at 12:24 PM ...
Launching pytest with arguments test_api_login.py::test_login_to_api --no-header --no-summary -q in /Users/myk/Documents/DUMP/apiLogin

============================= test session starts ==============================
collecting ... collected 1 item

test_api_login.py::test_login_to_api::test_my_test FAILED    [100%]
test_api_login.py:16 (test_login_to_api.test_my_test)
moto/core/models.py:118: in wrapper
    result = func(*args, **kwargs)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1322: in patched
    with self.decoration_helper(patched,
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/contextlib.py:113: in __enter__
    return next(self.gen)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1304: in decoration_helper
    arg = exit_stack.enter_context(patching)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/contextlib.py:425: in enter_context
    result = _cm_type.__enter__(cm)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1377: in __enter__
    self.target = self.getter()
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1552: in <lambda>
    getter = lambda: _importer(target)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1224: in _importer
    thing = __import__(import_path)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    import requests
    import json
    
    import boto3
    
    ssm = boto3.client('ssm', region_name="us-east-1")
    base_url_obj = ssm.get_parameters(Names=["/prof-svc-int/dev/api-base-url"])
>   BASE_URL = base_url_obj['Parameters'][0]['Value']
E   IndexError: list index out of range

lambda_function.py:8: IndexError




============================== 1 failed in 3.28s ===============================

Process finished with exit code 1

See the code for the main file being tested (lambda_function.py)

import requests
import json

import boto3

ssm = boto3.client('ssm', region_name="us-east-1")
base_url_obj = ssm.get_parameters(Names=["/prof-svc-int/dev/api-base-url"])
BASE_URL = base_url_obj['Parameters'][0]['Value']

username_obj = ssm.get_parameters(Names=["/prof-svc-int/dev/api-username"])
USERNAME = username_obj['Parameters'][0]['Value']

password_obj = ssm.get_parameters(Names=["/prof-svc-int/dev/api-password"])
PASSWORD = password_obj['Parameters'][0]['Value']


def lambda_handler(event, context):
    username = USERNAME
    password = PASSWORD

    message = ''
    code = 400

    headers = {"Accept": "application/json", "Content-Type": "application/json"}

    try:
        url = f"{BASE_URL}v0/login"

        data = {"username": username, "password": password}

        r = requests.post(url, headers=headers, data=json.dumps(data))
        message = r.json()

    except Exception as e:
        message += f'An error occurred  {str(e)}'

    if "token" in message:
        token = message['token']
        print("token >> " + token)
        put_response = ssm.put_parameter(
            Name='/prof-svc-int/dev/api-token',
            Value=token,
            Type='String',
            Overwrite=True
        )
        print("put_response >> ", put_response)
        code = 200
    output = OutputObj(code,
                       message)

    json_str = json.dumps(output.__dict__)

    result = json.loads(json_str)

    return result


class OutputObj:
    def __init__(self, statusCode, body):
        self.statusCode = statusCode
        self.body = body

See the code for test File (test_api_login.py)

import unittest
from unittest.mock import patch
from moto import mock_ssm


@mock_ssm
@patch("lambda_function.lambda_handler.base_url_obj")
@patch("lambda_function.lambda_handler.BASE_URL")
@patch("lambda_function.lambda_handler.username_obj")
@patch("lambda_function.lambda_handler.USERNAME")
@patch("lambda_function.lambda_handler.password_obj")
@patch("lambda_function.lambda_handler.PASSWORD")
class test_login_to_api(unittest.TestCase):
    @mock_ssm
    def test_my_test(self):

        with patch('lambda_function.lambda_handler') as mock_post:
            mock_post.return_value.statusCode = 200

            out = mock_post
            code = out.code
            self.assertEqual(code, 200)

    if __name__ == "__main__":
        unittest.main()
1

There are 1 best solutions below

0
On

You can use boto3 to recreate the same environment. Considering you are reading three SSM parameters, you can create these same parameters as part of the setUp-method:

@mock_ssm
@patch("lambda_function.lambda_handler.base_url_obj")
@patch("lambda_function.lambda_handler.BASE_URL")
@patch("lambda_function.lambda_handler.username_obj")
@patch("lambda_function.lambda_handler.USERNAME")
@patch("lambda_function.lambda_handler.password_obj")
@patch("lambda_function.lambda_handler.PASSWORD")
class test_login_to_api(unittest.TestCase):

    def setUp(self) -> None:
        ssm = boto3.client("ssm", "us-east-1")
        mock_values = [("/prof-svc-int/dev/api-base-url", "MY_BASE_URL"),
                       ("/prof-svc-int/dev/api-username", "MY_USERNAME"),
                       ("/prof-svc-int/dev/api-password", "MY_PASSWORD")]
        for key, val in mock_values:
            ssm.put_parameter(
                Name=key,
                Description=f"Mock value for {key}",
                Value=val,
                Type="String"
            )

    def test_my_test(self):
        # as usual
        # all parameters are now present in 'us-east-1'
        pass

Note that I've removed the mock_ssm-decorator on the test-method itself - you only need to use it on the class itself.