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

如何Mock AWS SDK测试Go语言实现的S3文件列表API端点

解决方案

核心问题原因

你的listFiles handler内部直接调用GetAWSClient()获取真实AWS客户端,硬编码的依赖导致测试时无法替换为Mock实例。

改造步骤

1. 解耦S3客户端依赖(修改业务代码)

你已经定义了S3ListObjectsAPI接口,我们通过依赖注入的方式将客户端传入handler,不要在handler内部直接获取真实客户端:

// 你已经定义好的接口,确保方法签名和S3 SDK的ListObjectsV2一致
type S3ListObjectsAPI interface {
    ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error)
}

// NewListFilesHandler 接收S3客户端接口,返回实际的handler函数
func NewListFilesHandler(client S3ListObjectsAPI) http.HandlerFunc {
    return func(w http.ResponseWriter, _ *http.Request) {
        bucket := aws.String(BUCKETNAME)
        input := &s3.ListObjectsV2Input{
            Bucket: bucket,
        }
        // 直接用传入的client调用方法,不需要再调用全局GetAWSClient
        resp, err := client.ListObjectsV2(context.TODO(), input)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        msg, _ := json.Marshal(resp.Contents)
        w.WriteHeader(200)
        w.Write(msg)
        return
    }
}

// 原有服务启动代码修改,传入真实AWS客户端
func handleRequest() {
    realS3Client := GetAWSClient()
    myRouter := mux.NewRouter().StrictSlash(true)
    // 注册路由时传入真实客户端构造的handler
    myRouter.HandleFunc("/listFiles", NewListFilesHandler(realS3Client)).Methods("GET")
    c := cors.Default()
    handler := c.Handler(myRouter)
    log.Fatal(http.ListenAndServe(":8080", handler))
}

如果你之前封装了GetObjects函数,需要将其参数修改为接收S3ListObjectsAPI接口类型,不要直接用s3.Client实体类型,确保Mock实例可以正常传入。

2. 调整测试逻辑,传入Mock客户端

测试时直接将你编写的Mock客户端传入handler构造函数,再用httptest发起请求即可,全程不会触发真实AWS调用:

func TestGetObjectFromS3(t *testing.T) {
    cases := []struct {
        client func(t *testing.T) S3ListObjectsAPI
        expect []byte
        expectStatus int
    }{
        {
            client: func(t *testing.T) S3ListObjectsAPI {
                return mockListObjectsAPI(func(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error) {
                    t.Helper()
                    if params.Bucket == nil {
                        t.Fatal("expect bucket to not be nil")
                    }
                    if e, a := "fooBucket", *params.Bucket; e != a {
                        t.Errorf("expect %v, got %v", e, a)
                    }
                    // 这里根据你的测试需要返回对应的Contents内容
                    return &s3.ListObjectsV2Output{
                        Name: aws.String("fooBucket"),
                        Contents: []types.Object{
                            {Key: aws.String("test1.txt"), Size: aws.Int64(123)},
                            {Key: aws.String("test2.txt"), Size: aws.Int64(456)},
                        },
                    }, nil
                })
            },
            // 预期的响应JSON,和上面返回的Contents序列化结果一致即可
            expect: []byte(`[{"Key":"test1.txt","Size":123},{"Key":"test2.txt","Size":456}]`),
            expectStatus: http.StatusOK,
        },
        // 可添加错误场景的测试用例,比如Mock返回错误校验接口错误处理逻辑
    }

    for i, tt := range cases {
        t.Run(strconv.Itoa(i), func(t *testing.T) {
            // 用Mock客户端构造handler
            testHandler := NewListFilesHandler(tt.client(t))
            // 构造测试请求和响应记录器
            req := httptest.NewRequest("GET", "/listFiles", nil)
            w := httptest.NewRecorder()
            // 执行请求
            testHandler.ServeHTTP(w, req)
            // 校验响应状态码
            if w.Code != tt.expectStatus {
                t.Errorf("expect status %d, got %d", tt.expectStatus, w.Code)
            }
            // 校验响应内容
            respBody, _ := io.ReadAll(w.Result().Body)
            w.Result().Body.Close()
            if !bytes.Equal(respBody, tt.expect) {
                t.Errorf("expect body %s, got %s", string(tt.expect), string(respBody))
            }
        })
    }
}

补充说明

  • 所有依赖外部服务的逻辑都可以通过依赖注入的方式解耦,测试时替换为Mock实现即可完全隔离外部依赖
  • 可以根据测试需要添加更多用例,比如Mock返回错误、返回空列表等场景,校验接口的异常处理逻辑是否符合预期

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 02:09:00