Reputation: 121
For sentence
resp, err := client.Get(fmt.Sprintf("https://www.xxxxx/day?time=%s", time))
If I want to mock a response to this client.Get() in unit test, I should use httptest.server, but how can I bind the url (https://www.xxxxx/day?time=%s) to the url of httptest.server? so that when I call client.Get() it can return the response I set before. For some reason I cannot mock a client here.
Upvotes: 8
Views: 8257
Reputation: 17778
Like this
func NewTestServerWithURL(URL string, handler http.Handler) (*httptest.Server, error) {
ts := httptest.NewUnstartedServer(handler)
if URL != "" {
l, err := net.Listen("tcp", URL)
if err != nil {
return nil, err
}
ts.Listener.Close()
ts.Listener = l
}
ts.Start()
return ts, nil
}
Upvotes: 6
Reputation: 31701
You don't, usually. You take the base URL from the server and give it to the client:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestClient(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify request, send mock response, etc.
}))
defer server.Close()
var client *http.Client
var time time.Time
baseURL := server.URL // Something like "http://127.0.0.1:53791"
resp, err := client.Get(fmt.Sprintf(baseURL+"/day?time=%s", time))
if err != nil {
t.Fatal(err)
}
// Verify response body if applicable
resp.Body.Close()
}
Upvotes: 5
Reputation: 27294
The http.Client is a struct not an interface which makes mocking it difficult as you have seen. An alternative way of mocking it is passing in the external dependencies that a routine needs, so instead of directly using client.Get, you use clientGet - which is a function pointer that was handed into the routine.
From the unit test you can then create :
mockClientGet(c *http.client, url string) (resp *http.Response, err error) {
// add the test code to return what you want it to.
}
Then in your main code use:
resp, err := clientGet(client, fmt.Sprintf("https://www.xxxxx/day?time=%s", time))
When calling the procedure normally, use the function pointer to http.Client.Get, and for your test pass in a pointer to your mock. It's not ideal, but I've not seen a nicer way around mocking non-interface external calls - and given its an external dependency, injecting it from the outside is not a bad thing.
Upvotes: 0