how to inject an url to httptest.server in golang?
Asked Answered
V

3

8

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.

Verney answered 26/10, 2017 at 0:37 Comment(1)
It would be helpful to post your code so we can see what you're trying to accomplish.Juieta
G
6

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
}
Groan answered 7/5, 2019 at 20:27 Comment(1)
Thanks a lot! This just saved my day - I couldn't figure out how to create a test server on a custom port and yours is the answer!Constancy
V
5

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()
}
Voluntarism answered 26/10, 2017 at 8:48 Comment(0)
D
0

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.

Dryclean answered 26/10, 2017 at 7:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.