rest - 使用/路径变量测试Chi路线

标签 rest go go-chi

我在测试go-chi路线(特别是带有路径变量的路线)时遇到了麻烦。使用go run main.go运行服务器可以正常工作,并且使用path变量对路由的请求的行为符合预期。

在对路由进行测试时,始终会收到HTTP错误:Unprocessable Entity。在注销articleID发生了什么之后,似乎articleCtx无法访问path变量。不知道这是否意味着我需要在测试中使用articleCtx,但是我尝试了ArticleCtx(http.HandlerFunc(GetArticleID))并收到错误消息:
panic: interface conversion: interface {} is nil, not *chi.Context [recovered] panic: interface conversion: interface {} is nil, not *chi.Context
运行服务器:go run main.go
测试服务器:go test .
我的来源:

// main.go

package main

import (
    "context"
    "fmt"
    "net/http"
    "strconv"

    "github.com/go-chi/chi"
)

type ctxKey struct {
    name string
}

func main() {
    r := chi.NewRouter()

    r.Route("/articles", func(r chi.Router) {
        r.Route("/{articleID}", func(r chi.Router) {
            r.Use(ArticleCtx)
            r.Get("/", GetArticleID) // GET /articles/123
        })
    })

    http.ListenAndServe(":3333", r)
}

// ArticleCtx gives the routes using it access to the requested article ID in the path
func ArticleCtx(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        articleParam := chi.URLParam(r, "articleID")
        articleID, err := strconv.Atoi(articleParam)
        if err != nil {
            http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
            return
        }

        ctx := context.WithValue(r.Context(), ctxKey{"articleID"}, articleID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// GetArticleID returns the article ID that the client requested
func GetArticleID(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    articleID, ok := ctx.Value(ctxKey{"articleID"}).(int)
    if !ok {
        http.Error(w, http.StatusText(http.StatusUnprocessableEntity), http.StatusUnprocessableEntity)
        return
    }

    w.Write([]byte(fmt.Sprintf("article ID:%d", articleID)))
}

// main_test.go

package main

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

func TestGetArticleID(t *testing.T) {
    tests := []struct {
        name           string
        rec            *httptest.ResponseRecorder
        req            *http.Request
        expectedBody   string
        expectedHeader string
    }{
        {
            name:         "OK_1",
            rec:          httptest.NewRecorder(),
            req:          httptest.NewRequest("GET", "/articles/1", nil),
            expectedBody: `article ID:1`,
        },
        {
            name:         "OK_100",
            rec:          httptest.NewRecorder(),
            req:          httptest.NewRequest("GET", "/articles/100", nil),
            expectedBody: `article ID:100`,
        },
        {
            name:         "BAD_REQUEST",
            rec:          httptest.NewRecorder(),
            req:          httptest.NewRequest("PUT", "/articles/bad", nil),
            expectedBody: fmt.Sprintf("%s\n", http.StatusText(http.StatusBadRequest)),
        },
    }

    for _, test := range tests {
        t.Run(test.name, func(t *testing.T) {
            ArticleCtx(http.HandlerFunc(GetArticleID)).ServeHTTP(test.rec, test.req)

            if test.expectedBody != test.rec.Body.String() {
                t.Errorf("Got: \t\t%s\n\tExpected: \t%s\n", test.rec.Body.String(), test.expectedBody)
            }
        })
    }
}

不知道如何继续。有任何想法吗?我想知道net/http/httptest中是否有关于将context与测试一起使用的答案,但未发现任何结果。

Go也很新(和context包),因此,任何代码复习/最佳实践注释都非常受人们欢迎:)

最佳答案

我对命名路径变量有同样的问题。我能够解决它,为我的测试设置路由器。格氏测试显示了一个很好的样本。
Go Chi Sample test with URL params

关于rest - 使用/路径变量测试Chi路线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54580582/

相关文章:

go - 为什么在检查完请求体后必须关闭请求体?

Go Dep - 在 dep 确保后缺少子包 "chi/middleware"

java - 基于 Jersey 的 RESTful 服务报告进度

json - mvc如何在不重定向到LogIn View 的情况下返回未经授权的代码

javascript - 这里的 map - 显示整个路线的地点

angularjs - 使用 Laravel 和 AngularJS 的 RESTful API

go - slice of slice 中的接口(interface)转换

Golang 的 "internal error: duplicate loads"- 如何解决这个错误?

go - 如何让 CORS 与 golang chi 服务器一起工作?

go - 有没有一种方法可以在多个处理程序中使用相同的request.Body,而无需手动编写大量代码,或者我需要更改执行此操作的方式?