Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test Go (Golang) API Outgoing Requests without Actually Hitting Third Party API

Tags:

testing

go

api

I'm working on a Go API that translates between an internal backend and several third party APIs. I'm trying to understand how to test its functionality without actually hitting an outside API.

For example, here's a server that handles incoming requests to make new songs, and sends the request to a third party API:

package main

import (
        "bytes"
        "encoding/json"
        "fmt"
        "net/http"
)

var ThirdPartyApi = "http://www.coolsongssite.api"

type IncomingRequest struct {
        username string         `json:"username"`
        password string         `json:"password"`
        songs    []IncomingSong `json:"songs"`
}

type OutgoingRequest struct {
        username string         `json:"username"`
        password string         `json:"password"`
        songs    []OutgoingSong `json:"songs"`
}

type IncomingSong struct {
        artist string `json:"artist"`
        album  string `json:"album"`
        title  string `json:"title"`
}

type OutgoingSong struct {
        musician string `json:"musician"`
        record   string `json:"record"`
        name     string `json:"name"`
}

func main() {
        http.HandleFunc("/songs/create", createSong)
        http.ListenAndServe(":8080", nil)
}

func createSong(rw http.ResponseWriter, req *http.Request) {
         decoder := json.NewDecoder(req.Body)
         var incomingRequest IncomingRequest
         decoder.Decode(&incomingRequest)
         outgoingRequest := incomingRequestToOutgoingRequest(incomingRequest)

         r, _ := json.Marshal(outgoingRequest)
         request, _ := http.NewRequest("POST", ThirdPartyApi, bytes.NewBuffer(r))
         request.Header.Set("Content-Type", "application/json")

         client := http.Client{}

         response, _ := client.Do(request)
         fmt.Fprintln(rw, response)
}

func incomingRequestToOutgoingRequest(inc IncomingRequest) OutgoingRequest {
        outgoingRequest := OutgoingRequest{
                username: inc.username,
                password: inc.password,
        }

        for _, s := range inc.songs {
                outgoingRequest.songs = append(
                        outgoingRequest.songs, OutgoingSong{
                                musician: s.artist,
                                record:   s.album,
                                name:     s.title,
                        },
                )
        }

        return outgoingRequest
}

So I might hit the app running on localhost:8080 with something like this:

curl -X POST http://localhost:8080/songs/new --data \
'{"username": "<my-username>", "password": "<my-password>", "songs": \
["artist": "<song-artist>", "title": "<song-title>", "album": "<song-album>"]}'

My question is: How do I write tests that test that the request that goes out (in this case to http://www.coolsongssite.api) is correct, without actually sending it?

Should I rewrite the createSong handler so I can isolate what is happening in client.Do(request)?

Any help/advice/points in the right direction are appreciated.

Here I can test incomingRequestToOutgoingRequest like so:

package main

import (
        "testing"
)

func TestincomingRequestToOutgoingRequest(t *testing.T) {
        incomingRequest := IncomingRequest{
                username: "myuser",
                password: "mypassword",
        }

        var songs []IncomingSong
        songs = append(
                songs, IncomingSong{
                artist: "White Rabbits",
                album:  "Milk Famous",
                title:  "I'm Not Me",
                },
        )

        outgoingRequest := incomingRequestToOutgoingRequest(incomingRequest)

        if outgoingRequest.songs[0].musician != "White Rabbits" {
                t.Error("Expected musican name to be 'White Rabbits'. Got: ", outgoingRequest.songs[0].musician)
        }
}
like image 388
Drew Ogryzek Avatar asked Jul 12 '16 21:07

Drew Ogryzek


1 Answers

You can use net/http/httptest.NewServer like this:

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(200)
    w.Write([]byte(`desired response here`))
}))
defer ts.Close()
ThirdPartyURL = ts.URL
...
your test here

Whenever your code does an HTTP call to the ThirdPartyURL you will receive the request inside the defined handler and can return any response you need.

like image 115
Tarsis Azevedo Avatar answered Oct 20 '22 20:10

Tarsis Azevedo