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