9

Google App Engine Go SDK 1.8.6 版支持本地单元测试。该appengine/aetest包允许我创建一个Context单元测试。

如何使用它net/http/httptest来测试我的 HTTP 处理程序?

4

2 回答 2

16

请参阅 goroot/src/pkg/appengine/aetest/context.go 的顶部(更新的源代码尚未发布在https://code.google.com/p/appengine-go)。乍一看,新的测试应用程序看起来是一个稍微强大/不同版本的appenginetesting,因此您可以进行相同类型的测试,请参阅此处了解如何使用 sampleHandler(w http.ResponseWriter, r *http.请求)被调用。

或者,您可以使 http.Handler 的 ContextHandler 如下所示:

type ContextHandler struct {
    Real func(*appengine.Context, http.ResponseWriter, *http.Request)
}

func (f ContextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    f.Real(c, w, r)
}

func myNewHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
// do something
}

然后您可以在 init() 中执行此操作以支持生产:

http.Handle("/myNewHandler", ContextHandler{myNewHandler})

这使得测试函数变得容易:

func TestMyNewHandler(t *testing.T) {
    c := aetest.NewContext()
    r, _ := http.NewRequest("GET", "/tasks/findOverdueSchedules", nil)
    w := httptest.NewRecorder()
    myNewHandler(c, w, r)
    if 200 != w.Code {
        t.Fail()
    }
}

以下是 appengine/aetest 中 context.go 的内容:

/* 包 aetest 提供了一个 appengine.Context 用于测试。

一个示例测试文件:包 foo_test

import (
    "testing"

    "appengine/memcache"
    "appengine/aetest"
)

func TestFoo(t *testing.T) {
    c, err := aetest.NewContext(nil)
    if err != nil {
        t.Fatal(err)
    }
    defer c.Close()

    it := &memcache.Item{
        Key:   "some-key",
        Value: []byte("some-value"),
    }
    err = memcache.Set(c, it)
    if err != nil {
        t.Fatalf("Set err: %v", err)
    }
    it, err = memcache.Get(c, "some-key")
    if err != nil {
        t.Fatalf("Get err: %v; want no error", err)
    }
    if g, w := string(it.Value), "some-value" ; g != w {
        t.Errorf("retrieved Item.Value = %q, want %q", g, w)
    }
}

环境变量 APPENGINE_API_SERVER 指定要使用的 api_server.py 可执行文件的位置。如果未设置,则查询系统 PATH。*/

于 2013-10-16T17:58:26.007 回答
1

如果您不反对使用Martini,那么依赖注入是解决问题的好方法。以下是设置测试的方法(使用Ginkgo):

var _ = Describe("Items", func() {

    var (
        m *martini.ClassicMartini
    )

    BeforeEach(func() {
        m = martini.Classic()

        // injects app engine context into requests
        m.Use(func(c martini.Context, req *http.Request) {
             con, _ := aetest.NewContext(nil)
             c.MapTo(con, (*appengine.Context)(nil))
        })

        m.Get("/items", func(c martini.Context){
             // code you want to test
        })
    })

    It("should get items", func() {
        recorder := httptest.NewRecorder()
        r, _ := http.NewRequest("GET", "/items", nil)
        m.ServeHTTP(recorder, r) // martini server used
        Expect(recorder.Code).To(Equal(200))
    })
})

在生产中,实际的上下文将被注入:

m.Use(func(c martini.Context, req *http.Request) {
    c.MapTo(appengine.NewContext(req), (*appengine.Context)(nil))
})
于 2014-04-16T23:05:55.573 回答