在尝试测试端点时如何设置Request.FormFile?
部分代码:
func (a *EP) Endpoint(w http.ResponseWriter, r *http.Request) {
...
x, err := strconv.Atoi(r.FormValue("x"))
if err != nil {
a.ren.Text(w, http.StatusInternalServerError, err.Error())
return
}
f, fh, err := r.FormFile("y")
if err != nil {
a.ren.Text(w, http.StatusInternalServerError, err.Error())
return
}
defer f.Close()
...
}
Run Code Online (Sandbox Code Playgroud)
如何使用httptest lib生成具有我在FormFile中可以获得的值的发布请求?
您不需要按照其他答案的建议模拟完整的 FormFile 结构。该mime/multipart包实现了一个 Writer 类型,可让您创建一个 FormFile。从文档
CreateFormFile 是一个围绕 CreatePart 的便捷包装器。它使用提供的字段名和文件名创建一个新的表单数据头。
func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, error)
Run Code Online (Sandbox Code Playgroud)
然后,您可以将此 io.Writer 传递给httptest.NewRequest,它接受读者作为参数。
request := httptest.NewRequest("POST", "/", myReader)
Run Code Online (Sandbox Code Playgroud)
为此,您可以将 FormFile 写入 io.ReaderWriter 缓冲区或使用 io.Pipe。这是一个使用管道的完整示例:
func TestUploadImage(t *testing.T) {
//Set up a pipe to avoid buffering
pr, pw := io.Pipe()
//This writers is going to transform
//what we pass to it to multipart form data
//and write it to our io.Pipe
writer := multipart.NewWriter(pw)
go func() {
defer writer.Close()
//we create the form data field 'fileupload'
//wich returns another writer to write the actual file
part, err := writer.CreateFormFile("fileupload", "someimg.png")
if err != nil {
t.Error(err)
}
//https://yourbasic.org/golang/create-image/
img := createImage()
//Encode() takes an io.Writer.
//We pass the multipart field
//'fileupload' that we defined
//earlier which, in turn, writes
//to our io.Pipe
err = png.Encode(part, img)
if err != nil {
t.Error(err)
}
}()
//We read from the pipe which receives data
//from the multipart writer, which, in turn,
//receives data from png.Encode().
//We have 3 chained writers !
request := httptest.NewRequest("POST", "/", pr)
request.Header.Add("Content-Type", writer.FormDataContentType())
response := httptest.NewRecorder()
handler := UploadFileHandler()
handler.ServeHTTP(response, request)
t.Log("It should respond with an HTTP status code of 200")
if response.Code != 200 {
t.Errorf("Expected %s, received %d", 200, response.Code)
}
t.Log("It should create a file named 'someimg.png' in uploads folder")
if _, err := os.Stat("./uploads/someimg.png"); os.IsNotExist(err) {
t.Error("Expected file ./uploads/someimg.png' to exist")
}
}
Run Code Online (Sandbox Code Playgroud)
此函数利用image包动态生成文件,利用您可以传递io.Writer给png.Encode. 同样,您可以通过多部分 Writer 生成 CSV 格式的字节(包“encoding/csv”中的 NewWriter),即时生成文件,而无需从文件系统中读取任何内容。
我将这些和其他答案合并到一个没有管道或 goroutine 的Echo示例中:
func Test_submitFile(t *testing.T) {
path := "testfile.txt"
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("object", path)
assert.NoError(t, err)
sample, err := os.Open(path)
assert.NoError(t, err)
_, err = io.Copy(part, sample)
assert.NoError(t, err)
assert.NoError(t, writer.Close())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", body)
req.Header.Set(echo.HeaderContentType, writer.FormDataContentType())
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetPath("/submit")
if assert.NoError(t, submitFile(c)) {
assert.Equal(t, 200, rec.Code)
assert.Contains(t, rec.Body.String(), path)
fi, err := os.Stat(expectedPath)
if os.IsNotExist(err) {
t.Fatal("Upload file does not exist", expectedPath)
}
assert.Equal(t, wantSize, fi.Size())
}
}
Run Code Online (Sandbox Code Playgroud)
如果你看一下FormFile函数的实现,你会看到它读取了暴露的MultipartForm字段.
https://golang.org/src/net/http/request.go?s=39022:39107#L1249
// FormFile returns the first file for the provided form key.
1258 // FormFile calls ParseMultipartForm and ParseForm if necessary.
1259 func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
1260 if r.MultipartForm == multipartByReader {
1261 return nil, nil, errors.New("http: multipart handled by MultipartReader")
1262 }
1263 if r.MultipartForm == nil {
1264 err := r.ParseMultipartForm(defaultMaxMemory)
1265 if err != nil {
1266 return nil, nil, err
1267 }
1268 }
1269 if r.MultipartForm != nil && r.MultipartForm.File != nil {
1270 if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
1271 f, err := fhs[0].Open()
1272 return f, fhs[0], err
1273 }
1274 }
1275 return nil, nil, ErrMissingFile
1276 }
Run Code Online (Sandbox Code Playgroud)
在您的测试中,您应该能够创建测试实例multipart.Form并将其分配给您的请求对象 - https://golang.org/pkg/mime/multipart/#Form
type Form struct {
Value map[string][]string
File map[string][]*FileHeader
}
Run Code Online (Sandbox Code Playgroud)
当然,这将要求您使用真正的文件路径,从测试的角度来看,这不是很好.要解决这个问题,您可以定义一个接口来FormFile从请求对象中读取并将模拟实现传递给您的EPstruct.
这是一篇很好的文章,其中包含一些如何执行此操作的示例:https://husobee.github.io/golang/testing/unit-test/2015/06/08/golang-unit-testing.html
通过结合以前的答案,这对我有用:
filePath := "file.jpg"
fieldName := "file"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
file, err := os.Open(filePath)
if err != nil {
t.Fatal(err)
}
w, err := mw.CreateFormFile(fieldName, filePath)
if err != nil {
t.Fatal(err)
}
if _, err := io.Copy(w, file); err != nil {
t.Fatal(err)
}
// close the writer before making the request
mw.Close()
req := httptest.NewRequest(http.MethodPost, "/upload", body)
req.Header.Add("Content-Type", mw.FormDataContentType())
res := httptest.NewRecorder()
// router is of type http.Handler
router.ServeHTTP(res, req)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2598 次 |
| 最近记录: |