Assume that when you call an endpoint in your application, it calls an external service. If you write a test for it without mocking the external service, your test will actually call the external service. To avoid such issue, you can mock it in your tests as shown below.


Consumer


This is the one that calls the external service behind the scene but we are not going to write a test for it. Instead we are going to pass the mock server's address to it in our test.


package pkg

import "net/http"

type Consumer struct {
ServiceAddress string
}

func (c Consumer) GetUsers() (*http.Response, error) {
req, err := http.NewRequest(http.MethodGet, c.ServiceAddress + "/server/api/v1/users", nil)
if err != nil {
return nil, err
}

client := http.Client{}
res, err := client.Do(req)
if err != nil {
return nil, err
}

return res, nil
}

Endpoint


package app

import (
"io/ioutil"
"net/http"

"internal/pkg"
)

type User struct {
ServiceConsumer pkg.Consumer
}

func (u User) Create(w http.ResponseWriter, r *http.Request) {
res, err := u.ServiceConsumer.GetUsers()
if err != nil {
_, _ = w.Write([]byte(err.Error()))
return
}

body, err := ioutil.ReadAll(res.Body)
if err != nil {
_, _ = w.Write([]byte(err.Error()))
return
}
res.Body.Close()

_, _ = w.Write(body)
}

Test


package app

import (
"net/http"
"net/http/httptest"
"testing"

"internal/pkg"
)

func TestUser_Create(t *testing.T) {
srv := serverMock()
defer srv.Close()

req := httptest.NewRequest(http.MethodGet, "/client/api/v1/users", nil)
res := httptest.NewRecorder()

user := User{ServiceConsumer: pkg.Consumer{ServiceAddress: srv.URL}}

user.Create(res, req)

if http.StatusOK != res.Code {
t.Error("expected", http.StatusOK, "got", res.Code)
}
if "mock server responding" != res.Body.String() {
t.Error("expected mock server responding got", res.Body.String())
}
}

func serverMock() *httptest.Server {
handler := http.NewServeMux()
handler.HandleFunc("/server/api/v1/users", usersMock)

srv := httptest.NewServer(handler)

return srv
}

func usersMock(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("mock server responding"))
}