使用aws-sdk-go-v2为DynamoDB的queryMyTable方法编写单元测试
解决aws-sdk-go-v2中DynamoDB客户端Mock问题及queryMyTable单元测试实现
你遇到的operation error DynamoDB: Query, expected endpoint resolver to not be nil错误,根源是自定义Mock客户端继承了dynamodb.Client——原生客户端需要完整的配置(包括endpoint resolver)才能初始化,即使是Mock也会触发内部配置校验。正确方案是抽象业务所需的DynamoDB接口,而非继承原生客户端,让Mock实现完全独立。
第一步:抽象DynamoDB操作接口
定义仅包含业务需要的Query方法的接口,让queryMyTable依赖这个接口,而非直接依赖*dynamodb.Client,这样Mock实现更灵活:
// DDBQueryClient 定义业务所需的DynamoDB查询接口 type DDBQueryClient interface { Query(ctx context.Context, params *dynamodb.QueryInput, optFns ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error) }
修改queryMyTable的参数,替换为抽象接口(原有业务逻辑保持不变):
func queryMyTable(ctx context.Context, dynamodbClient DDBQueryClient, tableName string, ids []string) (*dynamodb.QueryOutput, error) { // 原有逻辑代码保持不变 filterExpression := constructFilterExpression(ids) expressionAttributeValues := constructExpressionAttributeValues(ids) queryInput := &dynamodb.QueryInput{ TableName: aws.String("my-ddb-table"), IndexName: aws.String("my-key-index"), KeyConditionExpression: aws.String("my-key = :my-key"), FilterExpression: aws.String(filterExpression), ExpressionAttributeValues: expressionAttributeValues, ProjectionExpression: aws.String("id,list"), } result, err := dynamodbClient.Query(ctx, queryInput) if err != nil { return nil, err } return result, nil }
第二步:实现正确的Mock客户端
无需继承dynamodb.Client,直接实现DDBQueryClient接口,完全自定义逻辑:
// MockDDBQueryClient 实现DDBQueryClient接口的Mock客户端 type MockDDBQueryClient struct { QueryFunc func(ctx context.Context, params *dynamodb.QueryInput, optFns ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error) CapturedQueryInput *dynamodb.QueryInput // 捕获传入的查询参数,用于测试断言 } // Query 实现接口的Query方法 func (m *MockDDBQueryClient) Query(ctx context.Context, params *dynamodb.QueryInput, optFns ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error) { // 捕获传入的查询参数,供测试断言使用 m.CapturedQueryInput = params // 如果设置了自定义QueryFunc,优先执行 if m.QueryFunc != nil { return m.QueryFunc(ctx, params, optFns...) } // 默认返回空结果,可根据需求修改默认行为 return &dynamodb.QueryOutput{}, nil }
第三步:编写单元测试用例
使用Mock客户端覆盖成功、失败等场景,验证queryMyTable的逻辑正确性:
import ( "context" "testing" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/stretchr/testify/assert" ) func TestQueryMyTable_Success(t *testing.T) { ctx := context.TODO() // 准备测试数据与预期结果 testIDs := []string{"111", "222"} expectedItems := []map[string]types.AttributeValue{ { "id": &types.AttributeValueS{Value: "111"}, "list": &types.AttributeValueL{ Elements: []types.AttributeValue{&types.AttributeValueS{Value: "item1"}}, }, }, { "id": &types.AttributeValueS{Value: "222"}, "list": &types.AttributeValueL{ Elements: []types.AttributeValue{&types.AttributeValueS{Value: "item2"}}, }, }, } // 创建返回成功结果的Mock客户端 mockClient := &MockDDBQueryClient{ QueryFunc: func(ctx context.Context, params *dynamodb.QueryInput, optFns ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error) { return &dynamodb.QueryOutput{ Items: expectedItems, Count: aws.Int32(2), }, nil }, } // 调用待测试方法 result, err := queryMyTable(ctx, mockClient, "test-table", testIDs) // 断言结果正确性 assert.NoError(t, err) assert.Equal(t, int32(2), result.Count) assert.Equal(t, expectedItems, result.Items) // 断言查询参数是否符合预期 assert.Equal(t, "my-ddb-table", aws.ToString(mockClient.CapturedQueryInput.TableName)) assert.Equal(t, "my-key-index", aws.ToString(mockClient.CapturedQueryInput.IndexName)) assert.Equal(t, "id IN (:id1,:id2)", aws.ToString(mockClient.CapturedQueryInput.FilterExpression)) assert.Equal(t, "111", aws.ToString(mockClient.CapturedQueryInput.ExpressionAttributeValues[":id1"].(*types.AttributeValueS))) assert.Equal(t, "222", aws.ToString(mockClient.CapturedQueryInput.ExpressionAttributeValues[":id2"].(*types.AttributeValueS))) } func TestQueryMyTable_QueryError(t *testing.T) { ctx := context.TODO() // 创建返回错误的Mock客户端 mockClient := &MockDDBQueryClient{ QueryFunc: func(ctx context.Context, params *dynamodb.QueryInput, optFns ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error) { return nil, assert.AnError }, } // 调用待测试方法 result, err := queryMyTable(ctx, mockClient, "test-table", []string{"111"}) // 断言错误场景 assert.Error(t, err) assert.Nil(t, result) }
关键说明
- 避免继承原生Client:
dynamodb.Client内部包含大量配置依赖,继承会触发不必要的配置校验,用抽象接口可完全隔离原生实现细节。 - 参数捕获:通过
CapturedQueryInput可以验证queryMyTable生成的查询参数是否符合预期,确保constructFilterExpression等辅助方法的输出正确。 - 场景覆盖:通过自定义
QueryFunc可以轻松模拟成功、失败、空结果等各种业务场景,全面覆盖测试用例。
内容的提问来源于stack exchange,提问作者davidbilla
相关产品推荐
相关产品推荐

