unit-testing - 使用简单的 http 处理程序测试所有路径

标签 unit-testing testing go tdd

我正试图在这个简单的 http 处理程序文件上获得 100% 的代码覆盖率。

如果成功,该文件将写入默认响应 header ,然后返回 200 和我在下面测试过的“Pong”。但是,也有可能写入默认 header 会产生错误,在这种情况下,预期会出现带有内部错误正文的 500 响应。

我正在努力弄清楚如何在测试中触发 500 响应案例。如果出于某种原因将 writeDefaultHeaders 函数调用的第二个参数更改为“html”,则该案例将失败,因为 html 不是我的服务中受支持的响应内容类型。

在代码中模拟此调用/命中此错误分支的惯用方法是什么?

谢谢。

ping_handler_test.go

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func Test200PingHandler(t *testing.T) {
    req, _ := http.NewRequest("GET", "/ping", nil)
    w := httptest.NewRecorder()

    PingHandler(w, req)

    if w.Code != http.StatusOK {
        t.Errorf("Ping Handler Status Code is NOT 200; got %v", w.Code)
    }

    if w.Body.String() != "Pong" {
        t.Errorf("Ping Handler Response Body is NOT Pong; got %v", w.Body.String())
    }
}

// This fails as it is the same setup as the passing success case
func Test500PingHandler(t *testing.T) {
    req, _ := http.NewRequest("GET", "/ping", nil)
    w := httptest.NewRecorder()

    PingHandler(w, req)

    if w.Code != http.StatusInternalServerError {
        t.Errorf("Ping Handler Status Code is NOT 500; got %v", w.Code)
    }

    if w.Body.String() != "Internal Server Error" {
        t.Errorf("Ping Handler Response Body is NOT Internal Server Error; got %v", w.Body.String())
    }
}

func BenchmarkPingHandler(b *testing.B) {
    for i := 0; i < b.N; i++ {
        req, _ := http.NewRequest("GET", "/ping", nil)
        w := httptest.NewRecorder()

        PingHandler(w, req)
    }
}

ping_handler.go

package main

import (
    "fmt"
    "net/http"
)

func PingHandler(w http.ResponseWriter, r *http.Request) {
    err := writeDefaultHeaders(w, "text")
    if err != nil {
        handleException(w, err)
        return
    }

    fmt.Fprintf(w, "Pong")
}

func writeDefaultHeaders(w http.ResponseWriter, contentType string) error {
    w.Header().Set("X-Frame-Options", "DENY")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.Header().Set("X-XSS-Protection", "1;mode=block")

    switch contentType {
    case "text":
        w.Header().Set("Content-Type", "text/plain; charset=utf-8")
        return nil
    case "json":
        w.Header().Set("Content-Type", "application/json; charset=UTF-8")
        return nil
    default:
        return errors.New("Attempting to render an unknown content type")
    }
}

编辑 另一个例子:

json_response, err := json.Marshal(response)
if err != nil {
    handleException(w, err)
    return
}

在这种情况下,我该如何测试返回错误的 json.Marshal?

最佳答案

What is the idiomatic way to mock this call / hit this error branch in the code?

通常为了测试你想使用一个公共(public)接口(interface)并为你的代码提供一个实现(NewMyThing(hw HeaderWriter))或者使用一些其他机制(比如 DefaultHeaderWriter 你可以在你的测试中换掉它)。

由于此代码是私有(private)的,因此您可以只使用一个变量:

var writeDefaultHeaders = func(w http.ResponseWriter, contentType string) error {
    w.Header().Set("X-Frame-Options", "DENY")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.Header().Set("X-XSS-Protection", "1;mode=block")

    switch contentType {
    case "text":
        w.Header().Set("Content-Type", "text/plain; charset=utf-8")
        return nil
    case "json":
        w.Header().Set("Content-Type", "application/json; charset=UTF-8")
        return nil
    default:
        return errors.New("Attempting to render an unknown content type")
    }
}

func PingHandler(w http.ResponseWriter, r *http.Request) {
    err := writeDefaultHeaders(w, "text")
    if err != nil {
        handleException(w, err)
        return
    }

    fmt.Fprintf(w, "Pong")
}

然后在你的测试中把它换掉:

func Test500PingHandler(t *testing.T) {
    writeDefaultHeaders = headerWriterFunc(func(w http.ResponseWriter, contentType string) error {
        return fmt.Errorf("ERROR")
    })
    // ...
}

您可能想在完成后将其设置回去。

在我看来,像这样换掉一个函数并不是好的测试实践。测试应该针对公共(public) API,这样您就可以修改代码,而不必在每次进行单个更改时都重写测试。

接口(interface)示例:

type Marshaler interface {
    Marshal(v interface{}) ([]byte, error)
}

type jsonMarshaler struct{}

func (_ *jsonMarshaler) Marshal(v interface{}) ([]byte, error) {
    return json.Marshal(v)
}

var marshaler Marshaler = (*jsonMarshaler)(nil)

然后:

json_response, err := marshaler.Marshal(response)

关于unit-testing - 使用简单的 http 处理程序测试所有路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32448559/

相关文章:

PHP单元测试来自不同类的外部静态方法调用

c# - Assert.ReferenceEquals() 在 Visual Studio 测试中通过 Object.ReferenceEquals() 返回 'false'

java - AssertJ的 `usingComparatorForFields`是如何工作的?

python - 在生菜测试步骤中传入多个参数

go - 访问基本身份验证凭据

java - 无法获得 gomobile 绑定(bind)以使用 java

mysql - sql.Result.LastInsertId() 的线程安全

windows - 如何在 Windows 系统上测试 App Engine 项目?

java - 如何在没有 @runWith 注解的情况下初始化 zohhak 运行器

testing - 使测试用例可维护