首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >测试Go http.Request.FormFile?

测试Go http.Request.FormFile?
EN

Stack Overflow用户
提问于 2017-05-11 09:20:11
回答 4查看 10.1K关注 0票数 6

如何在尝试测试端点时设置Request.FormFile?

部分代码:

代码语言:javascript
复制
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()
    ...
}

如何使用httptest lib生成具有可以在FormFile中获取的值的post请求?

EN

回答 4

Stack Overflow用户

发布于 2019-07-18 05:21:02

您不需要按照另一个答案的建议模拟完整的FormFile结构。mime/multipart包实现了一个允许您创建FormFile的写入器类型。来自the docs

CreateFormFile是CreatePart的便捷包装器。它使用提供的字段名和文件名创建一个新的表单数据标题。

代码语言:javascript
复制
func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, error)

然后,您可以将此io.Writer传递给httptest.NewRequest,它接受一个读取器作为参数。

代码语言:javascript
复制
request := httptest.NewRequest("POST", "/", myReader)

为此,您可以将FormFile写入io.ReaderWriter缓冲区或使用io.Pipe。下面是一个使用管道的完整示例:

代码语言:javascript
复制
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")
    }
}

此函数利用image包动态生成一个文件,利用您可以将io.Writer传递给png.Encode这一事实。同样地,你可以通过你的多部分写入器来生成CSV格式的字节(在包“NewWriter/csv”中的编码),动态地生成一个文件,而不需要从你的文件系统中读取任何东西。

票数 11
EN

Stack Overflow用户

发布于 2017-05-11 10:40:37

如果您查看了FormFile函数的实现,就会看到它读取了公开的MultipartForm字段。

https://golang.org/src/net/http/request.go?s=39022:39107#L1249

代码语言:javascript
复制
        // 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  }

在测试中,您应该能够创建multipart.Form的测试实例并将其分配给请求对象-- https://golang.org/pkg/mime/multipart/#Form

代码语言:javascript
复制
type Form struct {
        Value map[string][]string
        File  map[string][]*FileHeader
}

当然,这将要求您使用真正的文件路径,这从测试的角度来看并不是很好。要解决这个问题,可以定义一个接口来从请求对象中读取FormFile,并将模拟实现传递到EP结构中。

这里有一个很好的帖子,里面有几个关于如何做到这一点的例子:https://husobee.github.io/golang/testing/unit-test/2015/06/08/golang-unit-testing.html

票数 8
EN

Stack Overflow用户

发布于 2020-07-27 20:20:05

我将这些答案和其他答案组合到一个没有管道或goroutines的Echo示例中:

代码语言:javascript
复制
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())
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43904974

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档