Moto 似乎没有在 pytest 中模拟 aws 交互
Moto does not appear to be mocking aws interactions in a pytest
假设我想模拟以下内容:
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')
我如何开始在 pytest 中模拟它?我可以通过创建虚拟 class 和必要的属性来创建模拟对象,但我怀疑这是错误的方法。
一些额外的细节,这是我要测试的内容:
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
但我似乎无法正确修补此问题。我正在努力做到这一点,以便我可以修补会话和该方法中的函数调用
我尝试使用 moto 并得到了这个:
@mock_sts
def test_check_aws_profile(self):
session = boto3.Session(profile_name='foo')
client = session.client('sts')
client.get_caller_identity().get('Account')
但我 运行 喜欢
> raise ProfileNotFound(profile=profile_name)
E botocore.exceptions.ProfileNotFound: The config profile (foo) could not be found
所以它似乎没有嘲笑任何东西:|
编辑:
事实证明,您需要在配置和凭据文件中拥有模拟凭据才能使其正常工作。
我不确定你到底想要什么,所以我会给你一些开始的东西。
例如,您让 unittest.mock
为您模拟一切。 (有用阅读:https://docs.python.org/3/library/unittest.mock.html)
module.py
:
import boto3
def function():
session = boto3.Session(profile_name="foobar")
client = session.resource("sts")
return client.get_caller_identity().get('Account')
test_module.py
:
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")
pytest 运行 的结果:
$ 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 =================================
我还建议安装 pytest-socket
和 运行 pytest --disable-socket
以确保您的测试不会意外与网络通信。
虽然使用 mock.patch
手动修补 boto 没有任何问题,但您也可以考虑使用更高级别的测试实用程序,例如 moto.
如果您想使用 moto
,您可以使用 AWS_SHARED_CREDENTIALS_FILE
环境变量,将其指向一个可以保存在测试文件夹中的虚拟凭据文件。
您可以在那里定义您的配置文件。示例:
文件:test_stuff.py. dummy_aws_credentials
test_stuff.py:
import os
from pathlib import Path
import boto3
import pytest
from moto import mock_sts
@pytest.fixture(scope='module')
def aws_credentials():
"""Mocked AWS Credentials for moto."""
moto_credentials_file_path = Path(__file__).parent.absolute() / 'dummy_aws_credentials'
os.environ['AWS_SHARED_CREDENTIALS_FILE'] = str(moto_credentials_file_path)
@mock_sts
def test_check_aws_profile(aws_credentials):
session = boto3.Session(profile_name='foo')
client = session.client('sts')
client.get_caller_identity().get('Account')
dummy_aws_credentials:
[foo]
aws_access_key_id = mock
aws_secret_access_key = mock
假设我想模拟以下内容:
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')
我如何开始在 pytest 中模拟它?我可以通过创建虚拟 class 和必要的属性来创建模拟对象,但我怀疑这是错误的方法。
一些额外的细节,这是我要测试的内容:
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
但我似乎无法正确修补此问题。我正在努力做到这一点,以便我可以修补会话和该方法中的函数调用
我尝试使用 moto 并得到了这个:
@mock_sts
def test_check_aws_profile(self):
session = boto3.Session(profile_name='foo')
client = session.client('sts')
client.get_caller_identity().get('Account')
但我 运行 喜欢
> raise ProfileNotFound(profile=profile_name)
E botocore.exceptions.ProfileNotFound: The config profile (foo) could not be found
所以它似乎没有嘲笑任何东西:|
编辑:
事实证明,您需要在配置和凭据文件中拥有模拟凭据才能使其正常工作。
我不确定你到底想要什么,所以我会给你一些开始的东西。
例如,您让 unittest.mock
为您模拟一切。 (有用阅读:https://docs.python.org/3/library/unittest.mock.html)
module.py
:
import boto3
def function():
session = boto3.Session(profile_name="foobar")
client = session.resource("sts")
return client.get_caller_identity().get('Account')
test_module.py
:
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")
pytest 运行 的结果:
$ 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 =================================
我还建议安装 pytest-socket
和 运行 pytest --disable-socket
以确保您的测试不会意外与网络通信。
虽然使用 mock.patch
手动修补 boto 没有任何问题,但您也可以考虑使用更高级别的测试实用程序,例如 moto.
如果您想使用 moto
,您可以使用 AWS_SHARED_CREDENTIALS_FILE
环境变量,将其指向一个可以保存在测试文件夹中的虚拟凭据文件。
您可以在那里定义您的配置文件。示例:
文件:test_stuff.py. dummy_aws_credentials
test_stuff.py:
import os
from pathlib import Path
import boto3
import pytest
from moto import mock_sts
@pytest.fixture(scope='module')
def aws_credentials():
"""Mocked AWS Credentials for moto."""
moto_credentials_file_path = Path(__file__).parent.absolute() / 'dummy_aws_credentials'
os.environ['AWS_SHARED_CREDENTIALS_FILE'] = str(moto_credentials_file_path)
@mock_sts
def test_check_aws_profile(aws_credentials):
session = boto3.Session(profile_name='foo')
client = session.client('sts')
client.get_caller_identity().get('Account')
dummy_aws_credentials:
[foo]
aws_access_key_id = mock
aws_secret_access_key = mock