From 5e399b683aff3a5f1d16688c99b0fa5432aad7ec Mon Sep 17 00:00:00 2001 From: Tom McCarthy Date: Wed, 9 Feb 2022 10:28:22 +0100 Subject: [PATCH 1/2] docs: Add testing section to docs for parameters utility --- docs/utilities/parameters.md | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 395f24b5a76..7e3efacb057 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -515,3 +515,76 @@ The **`config`** and **`boto3_session`** parameters enable you to pass in a cust value = ssm_provider.get("/my/parameter") ... ``` + +## Testing your code + +For unit testing your applications, you can mock the calls to the parameters utility to avoid calling AWS APIs. This +can be achieved in a number of ways - in this example, we use the [pytest monkeypatch fixture](https://docs.pytest.org/en/latest/how-to/monkeypatch.html) +to patch the `parameters.get_parameter` method: + +=== "tests.py" + ```python + from src.index import handler + + def test_handler(monkeypatch): + + def mockreturn(name): + return "mock_value" + + monkeypatch.setattr(parameters, "get_parameter", mockreturn) + return_val = handler({}, {}) + assert return_val.get('message') == 'mock_value' + + ``` + +=== "src/index.py" + ```python + from aws_lambda_powertools.utilities import parameters + + def handler(event, context): + # Retrieve a single parameter + value = parameters.get_parameter("my-parameter-name") + return {"message": value} + ``` + +If we need to use this pattern across multiple tests, we can avoid repetition by refactoring to use our own pytest fixture: + +=== "tests.py" + ```python + import pytest + + from src.index import handler + + @pytest.fixture + def mock_parameter_response(monkeypatch): + def mockreturn(name): + return "mock_value" + + monkeypatch.setattr(parameters, "get_parameter", mockreturn) + + # Pass our fixture as an argument to all tests where we want to mock the get_parameter response + def test_handler(mock_parameter_response): + return_val = handler({}, {}) + assert return_val.get('message') == 'mock_value' + + ``` + +Alternatively, if we need more fully featured mocking (for example checking the arguments passed to `get_parameter`), we +can use [unittest.mock](https://docs.python.org/3/library/unittest.mock.html) from the python stdlib instead of pytest's `monkeypatch` fixture. In this example, we use the +[patch](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch) decorator to replace the `aws_lambda_powertools.utilities.parameters.get_parameter` function with a [MagicMock](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock) +object named `get_parameter_mock`. + +=== "tests.py" + ```python + from unittest.mock import patch + + # Replaces "aws_lambda_powertools.utilities.parameters.get_parameter" with a Mock object + @patch("aws_lambda_powertools.utilities.parameters.get_parameter") + def test_handler(get_parameter_mock): + get_parameter_mock.return_value = 'mock_value' + + return_val = handler({}, {}) + get_parameter_mock.assert_called_with("my-parameter-name") + assert return_val.get('message') == 'mock_value' + + ``` From bae067d769b56415355cb730c88f96c3ce22079e Mon Sep 17 00:00:00 2001 From: Tom McCarthy Date: Wed, 9 Feb 2022 15:52:39 +0100 Subject: [PATCH 2/2] docs: Correct imported and monkeypatched paths --- docs/utilities/parameters.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 7e3efacb057..d02a3feb73a 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -524,17 +524,16 @@ to patch the `parameters.get_parameter` method: === "tests.py" ```python - from src.index import handler + from src import index def test_handler(monkeypatch): def mockreturn(name): return "mock_value" - monkeypatch.setattr(parameters, "get_parameter", mockreturn) - return_val = handler({}, {}) + monkeypatch.setattr(index.parameters, "get_parameter", mockreturn) + return_val = index.handler({}, {}) assert return_val.get('message') == 'mock_value' - ``` === "src/index.py" @@ -553,18 +552,18 @@ If we need to use this pattern across multiple tests, we can avoid repetition by ```python import pytest - from src.index import handler + from src import index @pytest.fixture def mock_parameter_response(monkeypatch): def mockreturn(name): return "mock_value" - monkeypatch.setattr(parameters, "get_parameter", mockreturn) + monkeypatch.setattr(index.parameters, "get_parameter", mockreturn) # Pass our fixture as an argument to all tests where we want to mock the get_parameter response def test_handler(mock_parameter_response): - return_val = handler({}, {}) + return_val = index.handler({}, {}) assert return_val.get('message') == 'mock_value' ``` @@ -577,13 +576,14 @@ object named `get_parameter_mock`. === "tests.py" ```python from unittest.mock import patch + from src import index # Replaces "aws_lambda_powertools.utilities.parameters.get_parameter" with a Mock object @patch("aws_lambda_powertools.utilities.parameters.get_parameter") def test_handler(get_parameter_mock): get_parameter_mock.return_value = 'mock_value' - return_val = handler({}, {}) + return_val = index.handler({}, {}) get_parameter_mock.assert_called_with("my-parameter-name") assert return_val.get('message') == 'mock_value'