基于AWS Rust Lambda Runtime与AWS Rust SDK的应用测试与Mock方案问询
Rust AWS Lambda + DynamoDB:离线测试与Mock AWS SDK指南
一、单元测试:直接Mock AWS SDK调用
AWS Rust SDK(如aws-sdk-dynamodb)本身提供了测试工具链,无需额外第三方库即可Mock服务调用。核心思路是通过自定义的Connector替换真实服务连接,返回预设响应。
示例代码(Mock DynamoDB GetItem操作):
use aws_sdk_dynamodb::{Client, Error, types::AttributeValue}; use aws_smithy_runtime::client::test_util::capture_requests; #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_dynamodb_get_item() -> Result<(), Error> { // 创建Mock客户端,捕获请求并返回预设响应 let (client, _request_recorder) = capture_requests(|req| { match req.operation_name() { "GetItem" => { let response = aws_sdk_dynamodb::operation::get_item::builders::GetItemOutputBuilder::default() .item("Id", AttributeValue::S("123".to_string())) .build() .unwrap(); Ok(response) } op => panic!("Unexpected operation: {}", op), } }); // 使用Mock客户端执行测试 let resp = client.get_item() .table_name("test-table") .key("Id", AttributeValue::S("123".to_string())) .send() .await?; assert_eq!(resp.item().unwrap().get("Id").unwrap().as_s().unwrap(), "123"); Ok(()) } }
二、离线手动测试:Cargo Lambda配合本地Mock服务
Cargo Lambda的watch命令仅负责热重载Lambda函数,本身不提供AWS服务Mock能力,需搭配本地AWS服务模拟器使用,推荐DynamoDB Local或LocalStack:
步骤1:启动本地Mock服务
以DynamoDB Local为例,用Docker启动:
docker run -p 8000:8000 amazon/dynamodb-local
步骤2:配置Cargo Lambda指向本地服务
运行Cargo Lambda时指定环境变量,让AWS SDK将请求发向本地模拟器:
AWS_ENDPOINT_URL=http://localhost:8000 AWS_REGION=us-east-1 cargo lambda watch
也可在项目根目录创建.env文件持久化配置:
AWS_ENDPOINT_URL=http://localhost:8000 AWS_REGION=us-east-1
步骤3:初始化本地测试资源
用AWS CLI连接本地服务创建测试表:
aws dynamodb create-table \ --table-name test-table \ --attribute-definitions AttributeName=Id,AttributeType=S \ --key-schema AttributeName=Id,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \ --endpoint-url http://localhost:8000
三、进阶:解耦业务与SDK,用Trait Mock
若需更灵活的Mock逻辑,可将DynamoDB操作抽象为Trait,通过mockall框架Mock该Trait,实现业务逻辑与SDK的解耦:
use aws_sdk_dynamodb::Client; use aws_sdk_dynamodb::types::AttributeValue; use aws_sdk_dynamodb::Error; // 定义抽象Trait trait DynamoDbOps { async fn get_item(&self, table_name: &str, id: &str) -> Result<Option<AttributeValue>, Error>; } // 真实服务实现 struct RealDynamoDb { client: Client, } impl DynamoDbOps for RealDynamoDb { async fn get_item(&self, table_name: &str, id: &str) -> Result<Option<AttributeValue>, Error> { let resp = self.client.get_item() .table_name(table_name) .key("Id", AttributeValue::S(id.to_string())) .send() .await?; Ok(resp.item().and_then(|item| item.get("Id").cloned())) } } // 单元测试用Mock #[cfg(test)] mod tests { use super::*; use mockall::mock; mock! { pub DynamoDbOps {} impl DynamoDbOps for DynamoDbOps { async fn get_item(&self, table_name: &str, id: &str) -> Result<Option<AttributeValue>, Error>; } } #[tokio::test] async fn test_lambda_handler() { let mut mock_db = MockDynamoDbOps::new(); mock_db.expect_get_item() .withf(|table, id| table == "test-table" && id == "123") .returning(|_, _| Ok(Some(AttributeValue::S("123".to_string())))); // 将Mock实例传入Lambda handler执行测试 let result = handler(mock_db).await; assert!(result.is_ok()); } }
内容的提问来源于stack exchange,提问作者Charlie
相关产品推荐
相关产品推荐

