如何测试从请求体中读取错误?

29

我正在为golang中的http处理程序编写单元测试。当查看运行此代码覆盖率报告时,我遇到了以下问题:从请求中读取请求正文时,ioutil.ReadAll可能会返回一个需要处理的错误。然而,当我为我的处理程序编写单元测试时,我不知道如何以触发此类错误的方式发送请求到我的处理程序(内容过早结束似乎不会生成此类错误,但会在解组身体时生成错误)。这就是我试图做的事情:

package demo

import (
    "bytes"
    "io/ioutil"
    "net/http"
    "net/http/httptest"
    "testing"
)

func HandlePostRequest(w http.ResponseWriter, r *http.Request) {
    body, bytesErr := ioutil.ReadAll(r.Body)
    if bytesErr != nil {
        // intricate logic goes here, how can i test it?
        http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
        return
    }
    defer r.Body.Close()
    // continue...
}

func TestHandlePostRequest(t *testing.T) {
    ts := httptest.NewServer(http.HandlerFunc(HandlePostRequest))
    data, _ := ioutil.ReadFile("testdata/fixture.json")
    res, err := http.Post(ts.URL, "application/json", bytes.NewReader(data))
    // continue...
}

我该如何编写一个测试用例来覆盖 HandlePostRequest 的情况,同时也涵盖了 bytesErr 不是 nil 的情况?

2个回答

52
你可以创建和使用自己伪造的http.Request,当读取请求体时故意返回一个错误。你不一定需要一个全新的请求,一个有问题的请求体就足够了(它是一个io.ReadCloser)。
最简单的方法是使用httptest.NewRequest()函数,在这里你可以传递一个io.Reader值作为请求体(被包装为io.ReadCloser)。
以下是一个示例io.Reader,当尝试从中读取时会故意返回一个错误:
type errReader int

func (errReader) Read(p []byte) (n int, err error) {
    return 0, errors.New("test error")
}

这个例子可以涵盖您的错误情况:

func HandlePostRequest(w http.ResponseWriter, r *http.Request) {
    defer r.Body.Close()
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        fmt.Printf("Error reading the body: %v\n", err)
        return
    }
    fmt.Printf("No error, body: %s\n", body)
}

func main() {
    testRequest := httptest.NewRequest(http.MethodPost, "/something", errReader(0))
    HandlePostRequest(nil, testRequest)
}

输出结果(在Go Playground上尝试):


Error reading the body: test error

如果您需要模拟来自响应正文(而不是请求正文)的错误读取,请参阅相关问题:如何强制在读取响应正文时出现错误


非常好的答案,正是我在寻找的。我的真实代码中有返回语句,但还是感谢您指出来,我会将其添加到问题中以避免其他人的困惑。 - m90

1

我提取了读取正文并处理响应的代码,然后使用以下方法创建了一个在读取正文时会失败的响应:

package main

import (
    "fmt"
    "io"
    "net/http"
)

type BrokenReader struct{}

func (br *BrokenReader) Read(p []byte) (n int, err error) {
    return 0, fmt.Errorf("failed reading")
}

func (br *BrokenReader) Close() error {
    return fmt.Errorf("failed closing")
}

func main() {
    headers := http.Header{
        "Content-Length": {"1"},
    }
    reader := BrokenReader{}

    resp := http.Response{
        Body:   &reader,
        Header: headers,
    }

    _, err := io.ReadAll(resp.Body)

    fmt.Println(err)
}

https://go.dev/play/p/_R9hI2AWm9G


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接