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)
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With