简体   繁体   English

如何使用httptest测试Go中的http调用

[英]How to test http calls in Go using httptest

I have the following code: 我有以下代码:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "time"
)

type twitterResult struct {
    Results []struct {
        Text     string `json:"text"`
        Ids      string `json:"id_str"`
        Name     string `json:"from_user_name"`
        Username string `json:"from_user"`
        UserId   string `json:"from_user_id_str"`
    }
}

var (
  twitterUrl = "http://search.twitter.com/search.json?q=%23UCL"
  pauseDuration = 5 * time.Second
)

func retrieveTweets(c chan<- *twitterResult) {
    for {
        resp, err := http.Get(twitterUrl)
        if err != nil {
            log.Fatal(err)
        }

        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        r := new(twitterResult) //or &twitterResult{} which returns *twitterResult
        err = json.Unmarshal(body, &r)
        if err != nil {
            log.Fatal(err)
        }
        c <- r
        time.Sleep(pauseDuration)
    }

}

func displayTweets(c chan *twitterResult) {
    tweets := <-c
    for _, v := range tweets.Results {
        fmt.Printf("%v:%v\n", v.Username, v.Text)
    }

}

func main() {
    c := make(chan *twitterResult)
    go retrieveTweets(c)
    for {
        displayTweets(c)
    }

}

I'd like to write some tests for it, but I'm not sure how to use the httptest package http://golang.org/pkg/net/http/httptest/ would appreciate some pointers 我想为它编写一些测试,但我不确定如何使用httptest包http://golang.org/pkg/net/http/httptest/会很感激一些指针

I came up with this (shamelessly copied from the tests for go OAuth https://code.google.com/p/goauth2/source/browse/oauth/oauth_test.go ): 我想出了这个(从测试中无耻地复制到了OAuth https://code.google.com/p/goauth2/source/browse/oauth/oauth_test.go ):

var request = struct {
    path, query       string // request
    contenttype, body string // response
}{
    path:        "/search.json?",
    query:       "q=%23Kenya",
    contenttype: "application/json",
    body:        twitterResponse,
}

var (
    twitterResponse = `{ 'results': [{'text':'hello','id_str':'34455w4','from_user_name':'bob','from_user_id_str':'345424'}]}`
)

func TestRetrieveTweets(t *testing.T) {
    handler := func(w http.ResponseWriter, r *http.Request) {

        w.Header().Set("Content-Type", request.contenttype)
        io.WriteString(w, request.body)
    }

    server := httptest.NewServer(http.HandlerFunc(handler))
    defer server.Close()

    resp, err := http.Get(server.URL)
    if err != nil {
        t.Fatalf("Get: %v", err)
    }
    checkBody(t, resp, twitterResponse)
}

func checkBody(t *testing.T, r *http.Response, body string) {
    b, err := ioutil.ReadAll(r.Body)
    if err != nil {
        t.Error("reading reponse body: %v, want %q", err, body)
    }
    if g, w := string(b), body; g != w {
        t.Errorf("request body mismatch: got %q, want %q", g, w)
    }
}

httptest does two types of tests: response and server httptest有两种类型的测试:响应和服务器

Response test: 反应测试:

func TestHeader3D(t *testing.T) {
    resp := httptest.NewRecorder()

    uri := "/3D/header/?"
    path := "/home/test"
    unlno := "997225821"

    param := make(url.Values)
    param["param1"] = []string{path}
    param["param2"] = []string{unlno}

    req, err := http.NewRequest("GET", uri+param.Encode(), nil)
    if err != nil {
            t.Fatal(err)
    }

    http.DefaultServeMux.ServeHTTP(resp, req)
    if p, err := ioutil.ReadAll(resp.Body); err != nil {
            t.Fail()
    } else {
            if strings.Contains(string(p), "Error") {
                    t.Errorf("header response shouldn't return error: %s", p)
            } else if !strings.Contains(string(p), `expected result`) {
                    t.Errorf("header response doen't match:\n%s", p)
            }
    }
}

Server test (which is what you need to use): 服务器测试(这是你需要使用的):

func TestIt(t *testing.T){
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintln(w, `{"fake twitter json string"}`)
    }))
    defer ts.Close()

    twitterUrl = ts.URL
    c := make(chan *twitterResult)
    go retrieveTweets(c)

    tweet := <-c
    if tweet != expected1 {
        t.Fail()
    }
    tweet = <-c
    if tweet != expected2 {
        t.Fail()
    }
}

BTW, you don't need to pass in the pointer of r, because it's already a pointer. 顺便说一下,你不需要传入r的指针,因为它已经是一个指针。

err = json.Unmarshal(body, r)

EDIT: for my recorder test, I could use my http handler like this: 编辑:对于我的录音机测试,我可以像这样使用我的http处理程序:

handler(resp, req)

But my original code is not using the default mux (but from Gorilla/mux), and I have some wrapping around the mux, eg insert server logging, and adding request context (Gorilla/context), so I had to start from mux and call ServeHTTP 但是我的原始代码没有使用默认的多路复用器(但是来自Gorilla / mux),我有一些环绕多路复用器,例如插入服务器日志记录,并添加请求上下文(Gorilla / context),所以我不得不从多路复用器开始致电ServeHTTP

最初这个代码片段是在GitHub Gist上找到的 ,但是在尝试将这个概念应用到我的一个项目时,我意识到我必须对主代码进行大量修改,因此我决定使用docker和curl通过集成测试来测试这些调用。

If you want to test your program, it is often best to write it with testing in mind. 如果您想测试您的程序,通常最好在考虑测试的情况下编写它。 For instance, if you extracted the inner loop of your retrieveTweets function into something like this: 例如,如果您将retrieveTweets函数的内部循环解压缩为如下所示:

func downloadTweets(tweetsUrl string) (*twitterResult, error)

You could invoke it with the URL of a test server you've set up using the httptest package without having to worry about the sleeps or repeated requests. 您可以使用您使用httptest包设置的测试服务器的URL调用它,而不必担心睡眠或重复请求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM