golang unit test for http handler with custom ServeHTTP implementation - unit-testing

I am trying to write unit test for my http file server.
I have implemented the ServeHTTP function so that it'd replace "//" with "/" in the URL:
type slashFix struct {
mux http.Handler
}
func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.Replace(r.URL.Path, "//", "/", -1)
h.mux.ServeHTTP(w, r)
}
The bare-minimum code would look like this:
func StartFileServer() {
httpMux := http.NewServeMux()
httpMux.HandleFunc("/abc/", basicAuth(handle))
http.ListenAndServe(":8000", &slashFix{httpMux})
}
func handle(writer http.ResponseWriter, r *http.Request) {
dirName := "C:\\Users\\gayr\\GolandProjects\\src\\NDAC\\download\\"
http.StripPrefix("/abc",
http.FileServer(http.Dir(dirName))).ServeHTTP(writer, r)
}
func basicAuth(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
if user != "UserName" || pass != "Password" {
w.WriteHeader(401)
w.Write([]byte("Unauthorised.\n"))
return
}
handler(w, r)
}
}
I came across instances like the following to test http handlers:
req, err := http.NewRequest("GET", "/abc/testfile.txt", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("UserName", "Password")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(basicAuth(handle))
handler.ServeHTTP(rr, req)
Doing so would invoke the ServeHTTP function implemented using http.HandleFunc, but I want ServeHTTP implemented in my code to be invoked. How can this be achieved? Also, is there a way for me to directly test StartFileServer()?
Edit: I checked the link provided in the comments; my question does not appear to be a duplicate. I have a specific question: instead of invoking the ServeHTTP function implemented using http.HandleFunc, I want ServeHTTP implemented in my code to be invoked. I do not see this addressed in the provided link.

http.HandlerFunc implements http.Handler. As Flimzy pointed out in the comments, there is no need for basicAuth to require a HandlerFunc; any http.Handler will do. Sticking to the http.Handler interface instead of the concrete HandlerFunc type will make everything easily composable:
func basicAuth(handler http.Handler) http.Handler { // Note: http.Handler, not http.HandlerFunc
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
if !ok {
// TODO
}
if user != "UserName" || pass != "Password" {
w.WriteHeader(401)
w.Write([]byte("Unauthorised.\n"))
return
}
handler.ServeHTTP(w, r)
})
}
func TestFoo(t *testing.T) {
req, err := http.NewRequest("GET", "/abc/testfile.txt", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("UserName", "Password")
rr := httptest.NewRecorder()
// composition is trivial now
sf := &slashFix{
mux: http.HandlerFunc(handle),
}
handler := basicAuth(sf)
handler.ServeHTTP(rr, req)
// assert correct rr
}

Related

How to unit test fasthttp-router handler properly?

I am trying to write unit test for my app. I am using fasthttp lib. I also use fasthttp-routing lib. So the problem is that my handler is not standard type of fasthttp.HandlerFunc but routing.Handler.
In order to test HTTP handlers i've written the function that accepts handler fasthttp.RequestHandler parameter. The lib method fasthttp.Serve() accepts handler with type fasthttp.RequestHandler. I use this method to serve incoming connections from the given listener using the given handler. But my handler is of type routing.Handler
My handler:
func deleteExampleBOById(c *routing.Context) error { // Some logic }
My serve() function that i use to serve connections in order to unit test handlers:
func serve(handler fasthttp.RequestHandler, req *http.Request) (*http.Response, error) {
ln := fasthttputil.NewInmemoryListener()
defer ln.Close()
go func() {
err := fasthttp.Serve(ln, handler)
if err != nil {
panic(fmt.Errorf("failed to serve: %v", err))
}
}()
client := http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return ln.Dial()
},
},
}
return client.Do(req)
}
My actual unit test:
func TestHandler(t *testing.T) {
r, err := http.NewRequest("GET", "http://localhost:8181/GoService/example/v1/1", nil)
if err != nil {
t.Error(err)
}
res, err := serve(getExampleBOById, r)
if err != nil {
t.Error(err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
fmt.Println(string(body))
}
I am not able to serve my handler using function fasthttp.serve(), because of signature differences. I would like to ask any ideas how to convert routung.Handler to fasthttp.HandlerFunc or any other ideas how to unit test my handlers.
I don't have ideas how to solve it

Golang: mock response testing http client

i'm new to Golang and i'm trying to write a test for a simple HTTP client.
i read a lot of ways of doing so also here in SO but none of them seems to work.
I'm having troubles mocking the client response
This is how my client looks right now:
type API struct {
Client *http.Client
}
func (api *API) MyClient(qp string) ([]byte, error) {
url := fmt.Sprintf("http://localhost:8000/myapi?qp=%s", qp)
resp, err := api.Client.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// handling error and doing stuff with body that needs to be unit tested
if err != nil {
return nil, err
}
return body, err
}
And this is my test function:
func TestDoStuffWithTestServer(t *testing.T) {
// Start a local HTTP server
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Write([]byte(`OK`))
}))
defer server.Close()
// Use Client & URL from our local test server
api := API{server.Client()}
body, _ := api.MyClient("1d")
fmt.Println(body)
}
As i said, this is how they look right cause i try lot of ways on doing so.
My problem is that i'm not able to mock the client respose. in this example my body is empty. my understanding was that rw.Write([]byte(OK)) should mock the response 🤔
In the end i solved it like this:
myclient:
type API struct {
Endpoint string
}
func (api *API) MyClient(slot string) ([]byte, error) {
url := fmt.Sprintf("%s/myresource?qp=%s", api.Endpoint, slot)
c := http.Client{}
resp, err := c.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, err
}
test:
func TestDoStuffWithTestServer(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Write([]byte(`{Result: [{Cluster_name: "cl1", Pings: 2}]}`))
}))
defer server.Close()
api := API{Endpoint: server.URL}
res, _ := api.MyClient("1d")
expected := []byte(`{Result: [{Cluster_name: "cl1", Pings: 2}]}`)
if !bytes.Equal(expected, res) {
t.Errorf("%s != %s", string(res), string(expected))
}
}
still, not 100% sure is the right way of doing so in Go

How to use http.ResponseWriter and http.request as argument(Unit Testing) with Golang language

I just getting started learning Golang and PostgreSQL. For now, I tried to make Unit testing for CreateTodo function.
My CreateTodo function is
func CreateTodo(w http.ResponseWriter, r *http.Request) {
CreateTodo := &models.Todo{}
utils.ParseBody(r, CreateTodo)
CreateTodoList := CreateTodo.CreateTodo()
res, _ := json.Marshal(CreateTodoList)
w.WriteHeader(http.StatusOK)
w.Write(res)
}
I tried to make Unit Test for this function... So far I wrote some codes like
func TestCreateTodo(t *testing.T) {
dbData := &models.Todo{
Title: "test-title-console-check",
Description: "test-description-console-check",
Condition: true,
}
utils.ParseBody(r, dbData) // r should be r *http.Request
submittedTodo := dbData.CreateTodo()
res, _ := json.Marshal(submittedTodo)
r.WriteHeader(http.StatusOK) // r should be r *http.Request
r.Write(res)
fmt.Println("res: ", res)
}
This is ParseBodu function in utils folder
func ParseBody(r *http.Request, x interface{}) {
if body, err := ioutil.ReadAll(r.Body); err == nil {
if err := json.Unmarshal([]byte(body), x); err != nil {
return
}
}
}
Here, I have a problem with passing net/http(r *http.Request). I am not sure how to pass this function like argument... I tried to receive it in TestCreateTodo(t *testing.T, r *http.Request) but not working what I expected.
Is there any way to unit test for CreateTodo function??
I really appreciate your help!
Edit 1]
I tried to make a global variable
var readData *http.Request
var writeData http.ResponseWriter
and using it in the function. The reason why I make it global variables is that I usually use it in the funcs like <w http.ResponseWriter, r *http.Request>, so I thought I can use as global vars too.
so I edit my code as
var readData *http.Request
var writeData http.ResponseWriter
func TestCreateTodo(t *testing.T) {
// w := httptest.NewRecorder()
dbData := &models.Todo{
Title: "test-title-console-check",
Description: "test-description-console-check",
Condition: true,
}
utils.ParseBody(readData, dbData)
submittedTodo := dbData.CreateTodo()
res, _ := json.Marshal(submittedTodo)
writeData.WriteHeader(http.StatusOK)
writeData.Write(res)
fmt.Println("res: ", res)
}
But I got this error
As mentioned by Volker, you need to create an http request. So you are missing this line:
req, err := http.NewRequest("GET", <your endpoint>, <your body>)
As shown by the Go http package documentation, the body must be passed as a stream of bytes. You can use bytes.Buffer for this:
var body bytes.Buffer
err := json.NewEncoder(&body).Encode(dbData)
After making your request, you need to initiate a response recorder and define the handler:
res := httptest.NewRecorder()
handler := http.HandlerFunc(<your handler>)
handler.ServeHTTP(res, req)
Then you can check if your response was as expected with the assert package.
~ Zoe ~

How to stub a method inside another

I'm writing a web app that will send requests to a third-party service to do some calculations, and send it back to the fronted.
Here are the relevant parts for the test I'm trying to writer.
client.go
func (c *ClientResponse) GetBankAccounts() (*BankAccounts, *RequestError) {
req, _ := http.NewRequest("GET", app.BuildUrl("bank_accounts"), nil)
params := req.URL.Query()
params.Add("view", "standard_bank_accounts")
req.URL.RawQuery = params.Encode()
c.ClientDo(req)
if c.Err.Errors != nil {
return nil, c.Err
}
bankAccounts := new(BankAccounts)
defer c.Response.Body.Close()
if err := json.NewDecoder(c.Response.Body).Decode(bankAccounts); err != nil {
return nil, &RequestError{Errors: &Errors{Error{Message: "failed to decode Bank Account response body"}}}
}
return bankAccounts, nil
}
helper.go
type ClientResponse struct {
Response *http.Response
Err *RequestError
}
type ClientI interface {
ClintDo(req *http.Request) (*http.Response, *RequestError)
}
func (c *ClientResponse) ClientDo(req *http.Request) {
//Do some authentication with third-party service
errResp := *new(RequestError)
client := http.Client{}
resp, err := client.Do(req)
if err != nil {
// Here I'm repourposing the third-party service's error response mapping
errResp.Errors.Error.Message = "internal server error. failed create client.Do"
}
c.Response = resp
c.Err = &errResp
}
I only want to test the GetBankAccounts() method so I want to stub the ClientDo, but I'm at a loss on how to do that. Here's what I have so far with my test case.
client_test.go
type StubClientI interface {
ClintDo(req *http.Request) (*http.Response, *RequestError)
}
type StubClientResponse struct {}
func (c *StubClientResponse) ClientDo(req *http.Request) (*http.Response, *RequestError) {
return nil, nil
}
func TestGetBankAccounts(t *testing.T) {
cr := new(ClientResponse)
accounts, err := cr.GetBankAccounts()
if err != nil {
t.Fatal(err.Errors)
}
t.Log(accounts)
}
The ClintDo still pointing to the actual method on the helper.go, how can I make it use the on in the test?
Update:
I've also tried the following and this doesn't work either, it still sends the request to actual third-party service.
client_test.go
func TestGetBankAccounts(t *testing.T) {
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, toJson(append(BankAccounts{}.BankAccounts, BankAccount{
Url: "https://foo.bar/v2/bank_accounts/1234",
Name: "Test Bank",
})))
}))
server := httptest.NewServer(mux)
cr := new(ClientResponse)
cr.Client = server.Client()
accounts, err := cr.GetBankAccounts()
if err != nil {
t.Fatal(err.Errors)
}
t.Log(accounts)
}
helper.go
type ClientResponse struct {
Client *http.Client
Response *http.Response
Err *RequestError
}
type ClientI interface {
ClintDo(req *http.Request) (*http.Response, *RequestError)
}
func (c *ClientResponse) ClientDo(req *http.Request) {
//Do some authentication with third-party service
errResp := *new(RequestError)
client := c.Client
resp, err := client.Do(req)
if err != nil {
// Here I'm repourposing the third-party service's error response mapping
errResp.Errors.Error.Message = "internal server error. failed create client.Do"
}
c.Response = resp
c.Err = &errResp
}
Update 2
I was able to make some progress from #dm03514 's answer but unfortunately, now I'm getting nil pointer exceptions on the test but not on actual code.
client.go
func (c *ClientResponse) GetBankAccounts() (*BankAccounts, *RequestError) {
req, _ := http.NewRequest("GET", app.BuildUrl("bank_accounts"), nil)
params := req.URL.Query()
params.Add("view", "standard_bank_accounts")
req.URL.RawQuery = params.Encode()
//cr := new(ClientResponse)
c.HTTPDoer.ClientDo(req)
// Panic occurs here
if c.Err.Errors != nil {
return nil, c.Err
}
bankAccounts := new(BankAccounts)
defer c.Response.Body.Close()
if err := json.NewDecoder(c.Response.Body).Decode(bankAccounts); err != nil {
return nil, &RequestError{Errors: &Errors{Error{Message: "failed to decode Bank Account response body"}}}
}
return bankAccounts, nil
}
helper.go
type ClientResponse struct {
Response *http.Response
Err *RequestError
HTTPDoer HTTPDoer
}
type HTTPDoer interface {
//Do(req *http.Request) (*http.Response, *RequestError)
ClientDo(req *http.Request)
}
type ClientI interface {
}
func (c *ClientResponse) ClientDo(req *http.Request) {
// This method hasn't changed
....
}
client_test.go
type StubDoer struct {
*ClientResponse
}
func (s *StubDoer) ClientDo(req *http.Request) {
s.Response = &http.Response{
StatusCode: 200,
Body: nil,
}
s.Err = nil
}
func TestGetBankAccounts(t *testing.T) {
sd := new(StubDoer)
cr := new(ClientResponse)
cr.HTTPDoer = HTTPDoer(sd)
accounts, err := cr.GetBankAccounts()
if err != nil {
t.Fatal(err.Errors)
}
t.Log(accounts)
}
=== RUN TestGetBankAccounts
--- FAIL: TestGetBankAccounts (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x12aae69]
There are two common ways to achieve this:
Dependency Injection using interfaces (your example)
Custom http.Transport, which has a hook you can override in your unit tests
It looks like you're close on the interface approach, and are lacking an explicit way to configure the concrete implementation. Consider an interface similiar to your ClientDo:
type HTTPDoer interface {
Do func(req *http.Request) (*http.Response, *RequestError)
}
Dependency injection has the caller configure depedencies and pass them into the resources that actually invoke those dependencies. In this case your ClientResponse struct would have a reference to a HTTPDoer:
type ClientResponse struct {
Response *http.Response
Err *RequestError
HTTPDoer HTTPDoer
}
This allows the caller to configure the concrete implementation that ClientResponse will invoke. In production this will be the actual http.Client but in test it could be anything that implements the Do function.
type StubDoer struct {}
func (s *StubDoer) Do(....)
The unit test could configure the StubDoer, then invoke GetBankAccounts and then make asserstion:
sd := &StubDoer{...}
cr := ClientResponse{
HTTPDoer: sd,
}
accounts, err := cr.GetBankAccounts()
// assertions
The reason it's called Dependency Injection is that the caller initializes the resource (StubDoer) and then provides that resource to the target (ClientResponse). ClientResponse knows nothing about the concrete implementation of HTTPDoer, only that it adheres to the interface!
I wrote a blog post that details dependency injection in the context of unit tests.

Mocking functions for handler unit test

I am stuck over testing with mocking, Here is my route for handler:
r.Handle("/users/{userID}", negroni.New(
negroni.HandlerFunc(validateTokenMiddleware),
negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
getUserDetailsHandler(w, r, db)
})),
)).Methods("GET")
And here is my handler:
func getUserDetailsHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
//Create UserDetailsView instance
var userview UserDetailsView
//Get varibale from mux
vars := mux.Vars(r)
//UserID fetches userId from vars
userID := vars["userID"]
//Get user Information by wpUsersID
wuis := store.NewWpUserInformationStore(db)
userInformation, _:= wuis.GetByID(uID)
json.NewEncoder(w).Encode(userview);
//Print result
w.WriteHeader(http.StatusOK)
}
And i mock the function which is in store package named as GetByID which is looks like this :
type wpUserInfoMockStore struct {
mock.Mock
}
func (m *wpUserInfoMockStore) GetByID(user *WpUserInformation) error {
rets := m.Called(user)
return rets.Error(0)
}
//InitMockStore store
func InitMockStore() *wpUserInfoMockStore {
s := new(wpUserInfoMockStore)
//store = s
return s
}
And i write test case for handler but i got an error cannot convert getUserDetailsHandler (type func(http.ResponseWriter, *http.Request, *sql.DB)) to type http.HandlerFunc but i can not find why is it happened, here i'm using reference for this https://github.com/sohamkamani/blog_example__go_web_db and here is my test case code:
func TestGetUserDetailsTes(t *testing.T) {
// Initialize the mock store
mockStore := store.InitMockStore()
mockStore.On("GetByID").Return([]*store.WpUserInformation{{
21,
sql.NullString{String: "john"},
sql.NullString{String: "Sorensen"},
0}}, nil).Once()
req, err := http.NewRequest("GET", "", nil)
//if requests gives error
if err != nil {
panic(err.Error())
}
//parameters for generateTestUserJWT are set
testUser.ID = "22"
testUser.UserName = "johns"
testUser.Depot = "NYC"
//JWT generated
refToken, err := generateTestJWT(testUser, false)
//handling error while generating token
if err != nil {
panic(err.Error())
}
//token returned is concatenated with Bearer string
newToken = "Bearer " + refToken
//request authorization header is set
req.Header.Set("Authorization", newToken)
req.Header.Set("Latitude", "123.12")
req.Header.Set("Longitude", "456.45")
//response is set
w := httptest.NewRecorder()
hf := http.HandlerFunc(getUserDetailsHandler)
hf.ServeHTTP(w, req)
//if response code is not statusOK then test fails
if w.Code != http.StatusOK {
t.Errorf("/users/{userID} GET request failed, got: %d, want: %d.", w.Code, http.StatusOK)
}
}
As you see i test handler without url like req, err := http.NewRequest("GET", "", nil) but when i used link inside then i can not able to use mock functions, here what am i missing/fault please help me out.
Thank you.
Use a middleware handler for generating the function. Pass a handler in your main function which will call your middleware returning http.handler. That way you can pass db object to your main data and which will call the middle ware returning handler.
func getUserDetailsHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) http.HandlerFunc{
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
//Create UserDetailsView instance
var userview UserDetailsView
//Get varibale from mux
vars := mux.Vars(r)
//UserID fetches userId from vars
userID := vars["userID"]
//Get user Information by wpUsersID
wuis := store.NewWpUserInformationStore(db)
userInformation, _:= wuis.GetByID(uID)
json.NewEncoder(w).Encode(userview);
//Print result
w.WriteHeader(http.StatusOK)
}
}