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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 22:15:09