Question:
Say I want to mock the following:
1 2 3 4 5 |
session = boto3.Session(profile_name=profile) resource = session.resource('iam') iam_users = resource.users.all() policies = resource.policies.filter(Scope='AWS', OnlyAttached=True, PolicyUsageFilter='PermissionsPolicy') |
How do I go about starting to mock this with in pytest? I could create mocked objects by creating a dummy class and the necessary attributes, but I suspect that’s the wrong approach.
Some additional details, here’s what I’m trying to test out:
1 2 3 4 5 6 7 8 9 10 11 12 |
def test_check_aws_profile(self, mocker): mocked_boto3 = mocker.patch('myapp.services.utils.boto3.Session') mocker.patch(mocked_boto3.client.get_caller_identity.get, return_value='foo-account-id') assert 'foo-account-id' == my_func('foo') #in myapp.services.utils.py def my_func(profile): session = boto3.Session(profile_name=profile) client = session.client('sts') aws_account_number = client.get_caller_identity().get('Account') return aws_account_number |
But I can’t quite seem to be able to get this patched correctly. I’m trying to make it so that I can patch session and the function calls in that method
I tried using moto and got this:
1 2 3 4 5 6 |
@mock_sts def test_check_aws_profile(self): session = boto3.Session(profile_name='foo') client = session.client('sts') client.get_caller_identity().get('Account') |
But I’m running into
1 2 3 |
> raise ProfileNotFound(profile=profile_name) E botocore.exceptions.ProfileNotFound: The config profile (foo) could not be found |
So it seems like it’s not mocking anything 😐
Edit:
Turns out you need to have the mocked credentials in a config and credentials file for this to work.
Answer:
I’m not sure what exactly you want, so I’ll give you something to start.
You let unittest.mock
to mock everything for you, for example. (Useful reading: https://docs.python.org/3/library/unittest.mock.html)
module.py
:
1 2 3 4 5 6 7 |
import boto3 def function(): session = boto3.Session(profile_name="foobar") client = session.resource("sts") return client.get_caller_identity().get('Account') |
test_module.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from unittest.mock import patch import module @patch("module.boto3") # this creates mock which is passed to test below def test_function(mocked_boto): # mocks below are magically created by unittest.mock when they are accessed mocked_session = mocked_boto.Session() mocked_client = mocked_session.resource() mocked_identity = mocked_client.get_caller_identity() # now mock the return value of .get() mocked_identity.get.return_value = "foo-bar-baz" result = module.function() assert result == "foo-bar-baz" # we can make sure mocks were called properly, for example mocked_identity.get.assert_called_once_with("Account") |
Results of pytest run:
1 2 3 4 5 6 7 8 9 10 |
$ pytest ================================ test session starts ================================ platform darwin -- Python 3.7.6, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 rootdir: /private/tmp/one collected 1 item test_module.py . [100%] ================================= 1 passed in 0.09s ================================= |
I would also recommend to install pytest-socket
and run pytest --disable-socket
to make sure your tests do not talk with network by accident.