Mock Boto3 AWS calls where client is global

633 Views Asked by At

I have some code that I am attempting to write unit tests for. I have looked at other answers here e.g. here although I have the problem that the Boto3 client reference is global and outside of the function I am testing.

A simplified version of the code under test:

Project structure:

▶ tree .                    
.
├── src
│   └── lib
│       ├── __init__.py
│       └── policy.py
└── tests
    └── test_policy.py

Code under test:

# src/lib/policy.py

import boto3

client = boto3.client("route53")

def get_policy_id(policy_name: str) -> str:
    response = client.list_traffic_policies()
    for policy in response["TrafficPolicySummaries"]:
        if policy["Name"] == policy_name:
            return policy["Id"]

    return ""

Test code:

# tests/test_policy.py

import sys
sys.path.insert(0, "./src/lib") # FIXME. Ignore.

import unittest
import boto3

from policy import get_policy_id
from unittest.mock import patch


class TestPolicy(unittest.TestCase):

    @patch("boto3.client.list_traffic_policies")
    @patch("boto3.client")
    def test_get_policy_id(self, client, list_traffic_policies):

        list_traffic_policies.return_value = {
            "TrafficPolicySummaries": [
                {
                    "Id": "e89c8276-483b-4b1b-a737-9016e0374066",
                    "Name": "*.example.com",
                    "Type": "A",
                    "LatestVersion": 1,
                    "TrafficPolicyCount": 1
                }
            ],
            "IsTruncated": False,
            "MaxItems": "100"
        }

        #client = boto3.client("route53")
        response = get_policy_id("*.example.com")

        client.assert_called_with("route53")
        list_traffic_policies.assert_called()

        self.assertEqual(response, "e89c8276-483b-4b1b-a737-9016e0374066")


def main():
    unittest.main()


if __name__ == "__main__":
    main()

When I run this:

▶ python3 tests/test_policy.py 
F
======================================================================
FAIL: test_get_policy_id (__main__.TestPolicy)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/alexharvey/.pyenv/versions/3.6.9/lib/python3.6/unittest/mock.py", line 1183, in patched
    return func(*args, **keywargs)
  File "tests/test_policy.py", line 37, in test_get_policy_id
    client.assert_called_with("route53")
  File "/Users/alexharvey/.pyenv/versions/3.6.9/lib/python3.6/unittest/mock.py", line 805, in assert_called_with
    raise AssertionError('Expected call: %s\nNot called' % (expected,))
AssertionError: Expected call: client('route53')
Not called

----------------------------------------------------------------------
Ran 1 test in 1.263s

FAILED (failures=1)

If I uncomment the line #client = boto3.client("route53") I get:

▶ python3 tests/test_policy.py 
F
======================================================================
FAIL: test_get_policy_id (__main__.TestPolicy)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/alexharvey/.pyenv/versions/3.6.9/lib/python3.6/unittest/mock.py", line 1183, in patched
    return func(*args, **keywargs)
  File "tests/test_policy.py", line 37, in test_get_policy_id
    client.assert_called_with("route53")
  File "/Users/alexharvey/.pyenv/versions/3.6.9/lib/python3.6/unittest/mock.py", line 805, in assert_called_with
    raise AssertionError('Expected call: %s\nNot called' % (expected,))
AssertionError: Expected call: mock('route53')
Not called

----------------------------------------------------------------------
Ran 1 test in 1.291s

FAILED (failures=1)

How do I set this all up correctly with a patched call to boto3 and passing tests?

0

There are 0 best solutions below