moto 没有嘲笑 ec2?

moto not mocking ec2?

我正在尝试测试一些使用 boto 的 python 代码。我不想尝试针对 AWS 进行集成测试,所以我试图用 moto 模拟它,但它的行为并不像我预期的那样。

测试代码如下:

import io
import boto3

from moto import mock_ec2
from unittest.mock import patch
from argparse import Namespace
from awswl import commands


@mock_ec2
@patch('awswl.externalip.get_external_ip', return_value='192.0.2.1')
def test_list_command_lists_ipv4_and_ipv6_cidrs(exip_method):
    # Given
    options = Namespace()
    options.sgid = "sg-123456"

    ec2 = boto3.resource('ec2')
    sg = ec2.create_security_group(
        Description='Security Group for SSH Whitelisting',
        GroupName='SSH Whitelist'
    )
    print("Created security group: {0}".format(sg.GroupId))

错误:

Testing started at 16:46 ...
/path/.virtualenvs/awswl-Ir8BWU8l/bin/python "/path/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-0/173.4301.16/PyCharm.app/Contents/helpers/pycharm/_jb_pytest_runner.py" --target test_commands.py::test_list_command_lists_ipv4_and_ipv6_cidrs
Launching py.test with arguments test_commands.py::test_list_command_lists_ipv4_and_ipv6_cidrs in /path/awswl/tests

============================= test session starts ==============================
platform darwin -- Python 3.6.4, pytest-3.4.2, py-1.5.2, pluggy-0.6.0
rootdir: /path/awswl, inifile:
collected 1 item
test_commands.py F
tests/test_commands.py:22 (test_list_command_lists_ipv4_and_ipv6_cidrs)
exip_method = <MagicMock name='get_external_ip' id='4547595848'>

    @mock_ec2
    @patch('awswl.externalip.get_external_ip', return_value='192.0.2.1')
    def test_list_command_lists_ipv4_and_ipv6_cidrs(exip_method):
        # Given
        options = Namespace()
        options.sgid = "sg-123456"

>       ec2 = boto3.resource('ec2')

test_commands.py:30: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/boto3/__init__.py:92: in resource
    return _get_default_session().resource(*args, **kwargs)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/boto3/session.py:389: in resource
    aws_session_token=aws_session_token, config=config)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/boto3/session.py:263: in client
    aws_session_token=aws_session_token, config=config)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/session.py:861: in create_client
    client_config=config, api_version=api_version)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/client.py:76: in create_client
    verify, credentials, scoped_config, client_config, endpoint_bridge)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/client.py:285: in _get_client_args
    verify, credentials, scoped_config, client_config, endpoint_bridge)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/args.py:45: in get_client_args
    endpoint_url, is_secure, scoped_config)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/args.py:111: in compute_client_args
    service_name, region_name, endpoint_url, is_secure)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/client.py:358: in resolve
    service_name, region_name)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/regions.py:122: in construct_endpoint
    partition, service_name, region_name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <botocore.regions.EndpointResolver object at 0x10f1a3908>
partition = OrderedDict([('defaults', OrderedDict([('hostname', '{service}.{region}.{dnsSuffix}'), ('protocols', ['https']), ('sig...1', OrderedDict()), ('us-east-2', OrderedDict()), ('us-west-1', OrderedDict()), ('us-west-2', OrderedDict())]))]))]))])
service_name = 'ec2', region_name = None

    def _endpoint_for_partition(self, partition, service_name, region_name):
        # Get the service from the partition, or an empty template.
        service_data = partition['services'].get(
            service_name, DEFAULT_SERVICE_DATA)
        # Use the partition endpoint if no region is supplied.
        if region_name is None:
            if 'partitionEndpoint' in service_data:
                region_name = service_data['partitionEndpoint']
            else:
>               raise NoRegionError()
E               botocore.exceptions.NoRegionError: You must specify a region.

../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/regions.py:135: NoRegionError
                                                       [100%]

=================================== FAILURES ===================================
_________________ test_list_command_lists_ipv4_and_ipv6_cidrs __________________

exip_method = <MagicMock name='get_external_ip' id='4547595848'>

    @mock_ec2
    @patch('awswl.externalip.get_external_ip', return_value='192.0.2.1')
    def test_list_command_lists_ipv4_and_ipv6_cidrs(exip_method):
        # Given
        options = Namespace()
        options.sgid = "sg-123456"

>       ec2 = boto3.resource('ec2')

test_commands.py:30: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/boto3/__init__.py:92: in resource
    return _get_default_session().resource(*args, **kwargs)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/boto3/session.py:389: in resource
    aws_session_token=aws_session_token, config=config)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/boto3/session.py:263: in client
    aws_session_token=aws_session_token, config=config)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/session.py:861: in create_client
    client_config=config, api_version=api_version)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/client.py:76: in create_client
    verify, credentials, scoped_config, client_config, endpoint_bridge)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/client.py:285: in _get_client_args
    verify, credentials, scoped_config, client_config, endpoint_bridge)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/args.py:45: in get_client_args
    endpoint_url, is_secure, scoped_config)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/args.py:111: in compute_client_args
    service_name, region_name, endpoint_url, is_secure)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/client.py:358: in resolve
    service_name, region_name)
../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/regions.py:122: in construct_endpoint
    partition, service_name, region_name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <botocore.regions.EndpointResolver object at 0x10f1a3908>
partition = OrderedDict([('defaults', OrderedDict([('hostname', '{service}.{region}.{dnsSuffix}'), ('protocols', ['https']), ('sig...1', OrderedDict()), ('us-east-2', OrderedDict()), ('us-west-1', OrderedDict()), ('us-west-2', OrderedDict())]))]))]))])
service_name = 'ec2', region_name = None

    def _endpoint_for_partition(self, partition, service_name, region_name):
        # Get the service from the partition, or an empty template.
        service_data = partition['services'].get(
            service_name, DEFAULT_SERVICE_DATA)
        # Use the partition endpoint if no region is supplied.
        if region_name is None:
            if 'partitionEndpoint' in service_data:
                region_name = service_data['partitionEndpoint']
            else:
>               raise NoRegionError()
E               botocore.exceptions.NoRegionError: You must specify a region.

../../../../../.virtualenvs/awswl-Ir8BWU8l/lib/python3.6/site-packages/botocore/regions.py:135: NoRegionError
=========================== 1 failed in 1.80 seconds ===========================
Process finished with exit code 0

它看起来像是在调用 boto,而不是用 Moto 模拟它,而且我得到了一个 NoRegionError 因为没有指定 region/profile。

我做错了什么?我假设是我,但我还没有弄清楚如何。 ;)

测试输出指出问题可能与使用 boto3 创建资源时未指定任何区域有关。 从您的测试输出:

botocore.exceptions.NoRegionError: You must specify a region.

同时检查 moto's tests,看起来区域也总是在此处指定。

只需使用示例区域初始化资源即可解决您的问题:

boto3.resource('ec2', region_name='eu-central-1')