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

Golang FormFile单元测试文件大小为0的问题解决

问题

我编写了如下Golang函数:

func CreateByID(w http.ResponseWriter, r *http.Request) {
    formFile, _, err := r.FormFile("audio")
    if err != nil {
        return
    }
    defer formFile.Close()
    defer r.MultipartForm.RemoveAll()

    tmpFile, err := os.CreateTemp("/tmp")
    if err != nil {
        return
    }
    defer os.Remove(tmpFile.Name())
    io.Copy(tmpFile, formFile)

    err = validateFile(tmpFile.Name())
    if err != nil {
        return
    }
}

func validateFile(path string) error {
    fs, err := os.Stat(path)
    if err != nil {
        return
    }
    if fs.Size() < allowedSize {
       return fmt.Errorf("file is too small")
    }
}

该函数从HTTP请求中获取名为audio的FormFile,生成临时文件后校验文件大小,实际运行正常,但单元测试时始终出现临时文件大小为0的问题。

我的测试用例如下:

func TestCreateByID(t *testing.T) {
    pr, pw := io.Pipe()
    writer := multipart.NewWriter(pw)

    go func() {
        defer writer.Close()
        _, err := writer.CreateFormFile("audio", "/tmp/audioFile")
        if err != nil {
            t.Error(err)
        }
        generateAudioSample("/tmp/audioFile")
    }()

    request := httptest.NewRequest(http.MethodGet, "/{id}")
    request.Header.Add("Content-Type", writer.FormDataContentType())

    response := httptest.NewRecorder()

    // Calling The Actual Function
    CreateByID(response, request)

    handler := http.NewServeMux()
    handler.ServeHTTP(response, request)

    if response.Code != 200 {
        t.Errorf("Expected %d, received %d", 200, response.Code)
        return
    }
}

其中generateAudioSample函数可正常生成非空文件,但io.Copy(tmpFile, formFile)始终生成空文件导致测试失败。请问需要对测试用例做哪些修改才能通过测试?

解决方案

测试用例存在几个关键问题,逐一修改即可解决:

  • 修正请求方法与请求体绑定
    文件上传必须用POST方法,GET请求不携带请求体;同时要将io.Pipe的读端绑定到请求的Body,否则请求无数据内容。修改后的请求创建代码:

    request := httptest.NewRequest(http.MethodPost, "/{id}", pr)
    request.Header.Set("Content-Type", writer.FormDataContentType())
    
  • 将音频内容写入multipart表单
    当前代码仅生成了本地音频文件,但未将内容写入到multipart表单的文件流中。需要打开生成的音频文件,把内容复制到表单文件的writer里:
    修改goroutine内的代码:

    go func() {
        defer writer.Close()
        // 创建表单文件的writer
        fw, err := writer.CreateFormFile("audio", "audioFile")
        if err != nil {
            t.Error(err)
            return
        }
        // 生成音频文件
        audioPath := "/tmp/audioFile"
        if err := generateAudioSample(audioPath); err != nil {
            t.Error(err)
            return
        }
        // 打开音频文件并复制到表单writer
        audioFile, err := os.Open(audioPath)
        if err != nil {
            t.Error(err)
            return
        }
        defer audioFile.Close()
        _, err = io.Copy(fw, audioFile)
        if err != nil {
            t.Error(err)
        }
    }()
    
  • 删除多余的ServeMux调用
    测试中已经直接调用了CreateByID,后续的handler.ServeHTTP(response, request)属于多余操作,会覆盖之前的响应记录,直接删除即可。

  • 修复原函数的validateFile错误
    validateFile中if err != nil { return }未返回错误,会导致文件状态获取失败时静默忽略,应改为:

    if err != nil {
        return err
    }
    

修改后的完整测试用例:

func TestCreateByID(t *testing.T) {
    pr, pw := io.Pipe()
    writer := multipart.NewWriter(pw)

    go func() {
        defer writer.Close()
        fw, err := writer.CreateFormFile("audio", "audioFile")
        if err != nil {
            t.Error(err)
            return
        }
        audioPath := "/tmp/audioFile"
        if err := generateAudioSample(audioPath); err != nil {
            t.Error(err)
            return
        }
        audioFile, err := os.Open(audioPath)
        if err != nil {
            t.Error(err)
            return
        }
        defer audioFile.Close()
        if _, err := io.Copy(fw, audioFile); err != nil {
            t.Error(err)
        }
    }()

    request := httptest.NewRequest(http.MethodPost, "/{id}", pr)
    request.Header.Set("Content-Type", writer.FormDataContentType())

    response := httptest.NewRecorder()

    CreateByID(response, request)

    if response.Code != 200 {
        t.Errorf("Expected %d, received %d", 200, response.Code)
        return
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:25:40