Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test http calls in Go using httptest

Tags:

http

go

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

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):

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)     } } 
like image 712
jwesonga Avatar asked Apr 22 '13 19:04

jwesonga


People also ask

What is httptest NewRecorder?

httptest. NewRequest provides a convenience wrapper around http. NewRequest so you don't have to check the error making a Request object. Below that httptest. NewRecorder makes a recorder that the HTTP handler writes to as its http.

What is Httptest?

The httptest. ResponseRecorder is an implementation of http. ResponseWriter and can be used to be passed into our server handler, record all the data that the handler will write to the response and return the data written afterwards.

What is response Writer Golang?

type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(statusCode int) } The Golang net/http Handler interface has serveHTTP method that takes the Response Writer interface as input and this allows the Golang HTTP Server to construct HTTP Response.


1 Answers

httptest does two types of tests: response and server

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.

err = json.Unmarshal(body, r) 

EDIT: for my recorder test, I could use my http handler like this:

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, e.g. insert server logging, and adding request context (Gorilla/context), so I had to start from mux and call ServeHTTP

like image 156
nemo Avatar answered Oct 13 '22 05:10

nemo