Re: Mocking out http Get call to an API

2,414 views
Skip to first unread message

Kamil Kisiel

unread,
Apr 26, 2013, 11:04:41 AM4/26/13
to golan...@googlegroups.com
What kind of pointers are you looking for? Your test looks fairly reasonable to me at a glance.

On Friday, April 26, 2013 5:49:43 AM UTC-7, John Wesonga wrote:
Ive written a simple twitter client that makes a http Get call to twitter's search API and returns a JSON feed. I want to mock out a http.Get call to the twitter API:

So far I have

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

func TestdownloadTweets(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, twitterResponse)
}))
defer ts.Close()
url := ts.URL + "?q=%23UCL"
r, err := http.Get(url)
if err != nil {
t.Fatalf("http.Get(%q) unexpected error: %v", url, err)
}
const expectedStatus = 200
if r.StatusCode != expectedStatus {
t.Fatalf("For %v, got status %d, want %d", url, r.StatusCode, expectedStatus)
}
}

Would appreciate some pointers

Jackie Li

unread,
Apr 26, 2013, 11:12:54 AM4/26/13
to johnw...@gmail.com, golang-nuts
I have replied your stack overflow question.

it looks ok but you don't need `url := ts.URL + "?q=%23UCL"` but use `url := ts.URL`

and maybe add 

w.Header().Set("Content-Type", "application/json")

in the http handler func 

(I know it's not important, but...)


On Fri, Apr 26, 2013 at 1:49 PM, <johnw...@gmail.com> wrote:
Ive written a simple twitter client that makes a http Get call to twitter's search API and returns a JSON feed. I want to mock out a http.Get call to the twitter API:

So far I have

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

func TestdownloadTweets(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, twitterResponse)
}))
defer ts.Close()
url := ts.URL + "?q=%23UCL"
r, err := http.Get(url)
if err != nil {
t.Fatalf("http.Get(%q) unexpected error: %v", url, err)
}
const expectedStatus = 200
if r.StatusCode != expectedStatus {
t.Fatalf("For %v, got status %d, want %d", url, r.StatusCode, expectedStatus)
}
}

Would appreciate some pointers

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Jackie

zeilund

unread,
May 27, 2013, 1:02:31 PM5/27/13
to John Wesonga, golan...@googlegroups.com
Hi John,

I would probably mock up a webserver and override the http.RoundTripper such that you can check both that request parameters are set as they are supposed to and so that you can return the expected json output to your calling method.

I wrote an api for Quizlet which might be of inspiration.

Here's my mockup:

func closeDummyServer(dummy_server *httptest.Server) {
        transport = nil
        dummy_server.Close()
}

func createDummyServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
        dummy_server := httptest.NewServer(http.HandlerFunc(handler))

        //change the host to use the test server
        SetTransport(&http.Transport{Proxy: func(*http.Request) (*url.URL, error) { return url.Parse(dummy_server.URL) }})

        //turn off SSL
        UseSSL = false

        return dummy_server
}

func returnDummyResponseForPath(path string, dummy_response string, t *testing.T) *httptest.Server {
        //serve dummy responses
        dummy_data := []byte(dummy_response)

        return createDummyServer(func(w http.ResponseWriter, r *http.Request) {
                if r.URL.Path != path {
                        fmt.Printf("path: %v", r.URL.Path)
                        t.Error("Path doesn't match")
                }
                w.Write(dummy_data)
        })
}


You can find the source on:
The files that are particular interesting are session.go session_test.go users_test.go and maybe the example dir. The code i wrote is highly inspired by another rest api client "Stack on go".

Hope it's of any use.

Kind Regards,
Jens Zeilund


On Mon, May 27, 2013 at 11:40 AM, John Wesonga <johnw...@gmail.com> wrote:
I came up with this:

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


On Friday, 26 April 2013 15:49:43 UTC+3, John Wesonga wrote:
Ive written a simple twitter client that makes a http Get call to twitter's search API and returns a JSON feed. I want to mock out a http.Get call to the twitter API:

So far I have

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

func TestdownloadTweets(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, twitterResponse)
}))
defer ts.Close()
url := ts.URL + "?q=%23UCL"
r, err := http.Get(url)
if err != nil {
t.Fatalf("http.Get(%q) unexpected error: %v", url, err)
}
const expectedStatus = 200
if r.StatusCode != expectedStatus {
t.Fatalf("For %v, got status %d, want %d", url, r.StatusCode, expectedStatus)
}
}

Would appreciate some pointers

Brad Rydzewski

unread,
May 27, 2013, 4:04:01 PM5/27/13
to golan...@googlegroups.com
We've been taking advantage of the fact that the http.RoundTripper (attached to the http.Client) is an interface.

When unit testing we override the DefaultClient's RoundTripper (Transport attribute) to our own Mock implementation. In our unit tests, we push mock responses onto a stack, and each time the RoundTrip function is invoked we pop the mock response off the stack, and use it to build an http.Response. Here is a snippet from our codebase:

func (t *Transort) RoundTrip(req *http.Request) (*http.Response, error) {
if t.Pop() == false {
return nil, ErrEmptyStack
}

// get the current response that was
// popped off the stack
resp := t.Current()

// mock response object
r := http.Response {
StatusCode : resp.Code,
Proto      : "HTTP/1.0",
ProtoMajor : 1,
  ProtoMinor : 0,
}

if len(resp.Body) > 0 {
buf := bytes.NewBuffer(resp.Body)
r.Body = ioutil.NopCloser(buf)
}

return &r, nil
Reply all
Reply to author
Forward
0 new messages