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