Golang: Http Testing Case ( gorilla/mux )

In our projects we have a lot of tests. Http Api tests are one of the most important parts of any testing.

Error Messages, Status Codes and Responses should be tested very careful.

This article contains:

  • Preparations
  • Test case
  • The magic function
  • The test
  • Conclusion

We will use a github.com/gorilla/mux for url routing.

Preparations

We are going to test a User handler. A handler which responses with a User profile.

func User(w http.ResponseWriter, r *http.Request) {
    ...
}

The router:

import (
	"github.com/gorilla/mux"
)

func NewRouter() (s *mux.Router) {
	s = mux.NewRouter()

	userHandler := s.PathPrefix("/user/").Subrouter()
	userHandler.HandleFunc("/", UserRegister).Methods("POST").Name("UserRegister")
	userHandler.HandleFunc("/{user_id}/", User).Methods("GET", "HEAD").Name("User")

	return s
}

Test case

In our test case we need to start a httptest.NewServer which will use a router:

import (
    ...
    "net/http/httptest"
    "net/url"
	"testing"
	...
)

func TestUserRegistration(t *testing.T) {
    router := NewRouter()
    localServer := httptest.NewServer(router)
    defer localServer.Close()
    localServerUrl, err := url.Parse(localServer.URL)

	...
}

We've got a localServerUrl which points to our endpont.

The magic function

We need to form a url for committing a request. Let's write a function for it

import (
	"github.com/gorilla/mux"
	"net/url"
)

func MakeUrl(server_url *url.URL, router *mux.Router, urlName string, url_params []string, query_params map[string]string) (*url.URL, error) {
	//  Get url from routing
	urlPtr, err := router.Get(urlName).URL(
		url_params...,
	)
	if err != nil {
		return nil, err
	}

	//  Get currently serving httptest
	url_I := *server_url
	//  Join it's address and path of a specific url
	url_I.Path = urlPtr.Path
	q := url_I.Query()
	for k, v := range query_params {
		q.Set(k, v)
	}
	url_I.RawQuery = q.Encode()

	return &url_I, err
}

This function makes Just Do It with an url :D

To form a url for getting a user's profile you need to call it:

    url, err := MakeUrl(
		localServerUrl,
		router,
		"UserProfile",        // <- HANDLER's NAME
		[]string{
			"user_id", 1234,  // <- HANDLER's URL PARAMS:   /users/123/
		},
		map[string]string{}{
			"format": "json", // <- HANDLER URL QUERY:      /users/123/?format=json
		}, 
	)
	Expect(err).NotTo(HaveOccurred())

Let's assume we have a complicated handler


    entityHandler.HandleFunc("/{entity_id}/{entity_property:(.*[\\\\/]?)}/", http_handlers.PutEntityProperty()).Methods("PUT", "OPTIONS").Name("PutEntityProperty")

To make a request you need to

    url, err := MakeUrl(
		localServerUrl,
		router,
		"PutEntityProperty",
		[]string{
			"entity_id", "users",
			"entity_property": "email"
		},
		map[string]string{}{
		}, 
	)
	Expect(err).NotTo(HaveOccurred())

The test

Our test will look like

	email := generated_data["email"]
	url, err := test_helpers.MakeUrl(
		localServerUrl,
		router,
		"UserRegister",
		[]string{},
		map[string]string{},
	)

	request := &TestHttpRequest{}
	err := request.Post(
		url.String(),
		map[string]interface{}{
			"Content-Type": "application/json"
		},
		map[string]interface{}{
			"email":    email,
			"password": generated_data["password"],
		},
	)

Conclusion

We have an easy http handlers testing. Just pass url's name, params and query params to function. In case if your endpoints will change - there is no need in editing a lot of tests to rename url :D