为何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
相关产品推荐
相关产品推荐

