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
相关产品推荐
相关产品推荐

