You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何pytest的moto上下文创建的DynamoDB表在测试中无法访问?

问题:Moto中Fixture创建的DynamoDB表在测试方法的mock上下文里无法访问

在pytest测试类中定义了一个fixture,通过mock_dynamodb()上下文创建了DynamoDB表,调试确认fixture执行成功且表已创建,但在测试方法中再次用mock_dynamodb()包裹后,找不到该表。

相关代码

测试类及fixture

class TestDynamodbClient:

    @pytest.fixture
    def test_table(self):
        with mock_dynamodb():
            table_name = "test_table"

            dynamodb = boto3.resource('dynamodb')

            params = {
                'TableName': table_name,
                'KeySchema': [
                    {'AttributeName': 'id', 'KeyType': 'HASH'},
                ],
                'AttributeDefinitions': [
                    {'AttributeName': 'id', 'AttributeType': 'N'},
                ],
                'ProvisionedThroughput': {
                    'ReadCapacityUnits': 5,
                    'WriteCapacityUnits': 5
                }
            }

            table = dynamodb.create_table(**params)
            table.wait_until_exists()

            assert table_name in [t.name for t in dynamodb.tables.all()]  # 验证创建成功

            return table_name

测试方法

@pytest.mark.integration
    def test_recreate_table__table_exists__recreates_table(self, test_table):
        with mock_dynamodb():
            # given
            client = DynamodbClient()

            # 验证表不存在
            dynamodb = boto3.resource('dynamodb')
            assert test_table in [t.name for t in dynamodb.tables.all()]  # 抛出AssertionError

            # when
            client.recreate_table("test_table")

            # then
            # ...

conftest.py配置

def pytest_configure(config):
    """Mocked AWS Credentials for moto."""
    os.environ["AWS_ACCESS_KEY_ID"] = "testing"
    os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"
    os.environ["AWS_SECURITY_TOKEN"] = "testing"
    os.environ["AWS_SESSION_TOKEN"] = "testing"
    os.environ["AWS_DEFAULT_REGION"] = "eu-central-1"
    os.environ["AWS_REGION"] = "eu-central-1"

    config.addinivalue_line(
        "markers",
        "integration: mark test to run only run as part of the integration test suite",
    )

原因分析
  • mock_dynamodb()上下文完全隔离:每个mock_dynamodb()上下文都会生成一个独立的模拟DynamoDB环境,Fixture中的上下文和测试方法里的上下文是两个毫无关联的环境,因此Fixture中创建的表不会出现在测试方法的上下文内。
  • Fixture上下文执行完毕后自动销毁:Fixture中的with mock_dynamodb()代码块执行结束时,对应的模拟环境会被立即清理,所有创建的表资源都会被销毁。测试方法拿到的只是表名字符串,但对应的模拟表已经不存在了。

解决方案

方案1:在Fixture中使用全局mock而非上下文管理器

修改Fixture,启动全局mock并确保在测试全程复用同一个模拟环境:

class TestDynamodbClient:

    @pytest.fixture
    def test_table(self):
        mock = mock_dynamodb()
        mock.start()
        try:
            table_name = "test_table"
            dynamodb = boto3.resource('dynamodb')
            params = {
                'TableName': table_name,
                'KeySchema': [
                    {'AttributeName': 'id', 'KeyType': 'HASH'},
                ],
                'AttributeDefinitions': [
                    {'AttributeName': 'id', 'AttributeType': 'N'},
                ],
                'ProvisionedThroughput': {
                    'ReadCapacityUnits': 5,
                    'WriteCapacityUnits': 5
                }
            }
            table = dynamodb.create_table(**params)
            table.wait_until_exists()
            assert table_name in [t.name for t in dynamodb.tables.all()]
            yield table_name
        finally:
            mock.stop()

测试方法中不再需要嵌套mock_dynamodb()上下文:

@pytest.mark.integration
def test_recreate_table__table_exists__recreates_table(self, test_table):
    # given
    client = DynamodbClient()
    dynamodb = boto3.resource('dynamodb')
    assert test_table in [t.name for t in dynamodb.tables.all()]

    # when
    client.recreate_table("test_table")

    # then
    # ...

方案2:让测试逻辑复用Fixture的mock上下文

通过Fixture传递mock上下文,确保测试方法和Fixture在同一个模拟环境中运行:

@pytest.fixture
def dynamodb_mock():
    with mock_dynamodb() as mock:
        yield mock

@pytest.fixture
def test_table(dynamodb_mock):
    table_name = "test_table"
    dynamodb = boto3.resource('dynamodb')
    params = {
        'TableName': table_name,
        'KeySchema': [
            {'AttributeName': 'id', 'KeyType': 'HASH'},
        ],
        'AttributeDefinitions': [
            {'AttributeName': 'id', 'AttributeType': 'N'},
        ],
        'ProvisionedThroughput': {
            'ReadCapacityUnits': 5,
            'WriteCapacityUnits': 5
        }
    }
    table = dynamodb.create_table(**params)
    table.wait_until_exists()
    assert table_name in [t.name for t in dynamodb.tables.all()]
    return table_name

测试方法直接依赖fixture即可,无需额外添加上下文:

@pytest.mark.integration
def test_recreate_table__table_exists__recreates_table(self, test_table):
    client = DynamodbClient()
    dynamodb = boto3.resource('dynamodb')
    assert test_table in [t.name for t in dynamodb.tables.all()]

    client.recreate_table("test_table")

    # 后续断言逻辑

内容的提问来源于stack exchange,提问作者oschlueter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:17:26