Mocking functions for handler unit test - unit-testing

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)
}
}

Related

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.

Making a Golang business method with multiple dependencies testable

I have a job as a unit-tester, and there's a couple of functions that, as they are, are untestable. I have tried telling my immediate boss this, and he's telling me that I cannot refactor the code to make it testable. I will bring it up in today's meeting, but first, I want to make sure that I have a solid plan on doing the refactoring such that the business use case doesn't change.
The method
The method itself is defined like this:
//SendRequest This is used to contact the apiserver synchronously.
func (apiPath *APIPath) SendRequest(context interface{}, tokenHandler *apiToken.APITokenHandlerSt,
header map[string]string,
urlParams []string, urlQueries url.Values,
jsonBody []byte) apiCore.CallResultSt {
if apiToken := tokenHandler.GetToken(apiPath.AuthType, apiPath.Scope); apiToken != nil {
return apiPath.APICoreHandler.SendRequest(
context,
apiToken.Token,
apiPath.GetPath(urlParams, urlQueries), apiPath.Type,
header, jsonBody)
} else {
errMsg, _ := json.Marshal(errors.InvalidAuthentication())
return apiCore.CallResultSt{DetailObject: errMsg, IsSucceeded: false}
}
}
where its receiver object is defined thus:
//APIPath=======================
//Used for url construction
type APIPath struct {
APICoreHandler *apiCore.APICoreSt
// domain name of API
DomainPath string
ParentAPI *APIPath
Type apiCore.APIType
// subfunction name
SubFunc string
KeyName string
AutoAddKeyToPath bool
AuthType oAuth2.OAuth2Type
Scope string
}
Dependencies
You may have observed at least two of them: tokenHandler.GetToken and APICoreHandler.SendRequest
The definitions of those, and their objects are as follows:
tokenHandler
type APITokenHandlerSt struct {
Tokens []APITokenSt
}
tokenHandler.GetToken
// GetToken returns the token having the specified `tokenType` and `scope`
//
// Parameters:
// - `tokenType`
// - `scope`
//
// Returns:
// - pointer to Token having `tokenType`,`scope` or nil
func (ath *APITokenHandlerSt) GetToken(tokenType oAuth2.OAuth2Type, scope string) *APITokenSt {
if ath == nil {
return nil
}
if i := ath.FindToken(tokenType, scope); i == -1 {
return nil
} else {
return &ath.Tokens[i]
}
}
APICoreHandler
type APICoreSt struct {
BaseURL string
}
APICoreHandler.SendRequest
//Establish the request to send to the server
func (a *APICoreSt) SendRequest(context interface{}, token string, apiURL string, callType APIType, header map[string]string, jsonBody []byte) CallResultSt {
if header == nil {
header = make(map[string]string)
}
if header["Authorization"] == "" {
header["Authorization"] = "Bearer " + token
}
header["Scope"] = GeneralScope
header["Content-Type"] = "application/json; charset=UTF-8"
return a.CallServer(context, callType, apiURL, header, jsonBody)
}
APICoreHandler.CallServer
//CallServer Calls the server
//
// Parameters:
// - `context` : a context to pass to the server
// - `apiType` : the HTTP method (`GET`,`POST`,`PUT`,`DELETE`,...)
// - `apiURL` : the URL to hit
// - `header` : request header
// - `jsonBody`: the JSON body to send
//
// Returns:
// - a CallResultSt. This CallResultSt might have an error for its `DetailObject`
func (a *APICoreSt) CallServer(context interface{}, apiType APIType, apiURL string, header map[string]string, jsonBody []byte) CallResultSt {
var (
Url = a.BaseURL + apiURL
err error
res *http.Response
resBody json.RawMessage
hc = &http.Client{}
req = new(http.Request)
)
req, err = http.NewRequest(string(apiType), Url, bytes.NewBuffer(jsonBody))
if err != nil {
//Use a map instead of errorSt so that it doesn't create a heavy dependency.
errorSt := map[string]string{
"Code": "ez020300007",
"Message": "The request failed to be created.",
}
logger.Instance.LogError(err.Error())
err, _ := json.Marshal(errorSt)
return CallResultSt{DetailObject: err, IsSucceeded: false}
}
for k, v := range header {
req.Header.Set(k, v)
}
res, err = hc.Do(req)
if res != nil {
resBody, err = ioutil.ReadAll(res.Body)
res.Body = ioutil.NopCloser(bytes.NewBuffer(resBody))
}
return CallResultSt{resBody, logger.Instance.CheckAndHandleErr(context, res)}
}
My progress thus far
Obviously, tokenHandler has no business being passed in as an object, especially when its state is not being used. Thus, making that testable would be as simple as create a one-method interface, and use it instead of the *apiToken.APITokenHandlerSt
My concern, however, is with that APICoreHandler and its SendRequest method. I would like to know how to refactor it such that the use case of this code under test doesn't change, whilst allowing me to control this.
This is imperative, because most of the methods I have yet to test, hit apiPath.SendRequest somehow
UPDATE: I made the following test attempt, which caused panic:
func TestAPIPath_SendRequest(t *testing.T) {
// create a fake server that returns a string
fakeServer := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello world!")
}))
defer fakeServer.Close()
// define some values
scope := "testing"
authType := oAuth2.AtPassword
// create a tokenHandler
tokenHandler := new(apiToken.APITokenHandlerSt)
tokenHandler.Tokens = []apiToken.APITokenSt{
apiToken.APITokenSt{
Scope: scope,
TokenType: authType,
Token: "dummyToken",
},
}
// create some APIPaths
validAPIPath := &APIPath{
Scope: scope,
AuthType: authType,
}
type args struct {
context interface{}
tokenHandler *apiToken.APITokenHandlerSt
header map[string]string
urlParams []string
urlQueries url.Values
jsonBody []byte
}
tests := []struct {
name string
apiPath *APIPath
args args
want apiCore.CallResultSt
}{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.apiPath.SendRequest(tt.args.context, tt.args.tokenHandler, tt.args.header, tt.args.urlParams, tt.args.urlQueries, tt.args.jsonBody); !reflect.DeepEqual(got, tt.want) {
t.Errorf("APIPath.SendRequest() = %v, want %v", got, tt.want)
}
})
}
t.Run("SanityTest", func(t *testing.T) {
res := validAPIPath.SendRequest("context",
tokenHandler,
map[string]string{},
[]string{},
url.Values{},
[]byte{},
)
assert.True(t,
res.IsSucceeded)
})
}

golang unit test for http handler with custom ServeHTTP implementation

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
}

Unit Testing App Engine with Gorilla MUX

I want to write tests for handlers in Google App Engine that use Gorilla mux to read variables from the request URL.
I understand from the documentation that you can create a fake context and request to use with testing.
I'm calling the handler directly in the test but the handler isn't seeing the path parameter as expected.
func TestRouter(t *testing.T) {
inst, _ := aetest.NewInstance(nil) //ignoring error for brevity
defer inst.Close()
//tried adding this line because the test would not work with or without it
httptest.NewServer(makeRouter())
req, _ := inst.NewRequest("GET", "/user/john#example.com/id-123", nil)
req.Header.Add("X-Requested-With", "XMLHttpRequest")
resp := httptest.NewRecorder()
restHandler(resp, req)
}
func restHandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
params := mux.Vars(r)
email := params["email"]
//`email` is always empty
}
The problem is that the handler always sees an empty "email" parameter because the path is not interpreted by Gorilla mux.
The router is as below:
func makeRouter() *mux.Router {
r := mux.Router()
rest := mux.NewRouter().Headers("Authorization", "").
PathPrefix("/api").Subrouter()
app := r.Headers("X-Requested-With", "XMLHttpRequest").Subrouter()
app.HandleFunc("/user/{email}/{id}", restHandler).Methods(http.MethodGet)
//using negroni for path prefix /api
r.PathPrefx("/api").Handler(negroni.New(
negroni.HandlerFunc(authCheck), //for access control
negroni.Wrap(rest),
))
return r
}
All my searches have not gotten anything specific to App Engine unit testing with Gorilla mux.
Since what you're testing is the handler, you could just get an instance of the router and call ServeHTTP on it. Here is how it should be based on your code.
main.go
func init() {
r := makeRouter()
http.Handle("/", r)
}
func makeRouter() *mux.Router {
r := mux.NewRouter()
app := r.Headers("X-Requested-With", "XMLHttpRequest").Subrouter()
app.HandleFunc("/user/{email}/{id}", restHandler).Methods(http.MethodGet)
return r
}
func restHandler(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
email := params["email"]
fmt.Fprintf(w, email)
}
main_test.go
func TestRouter(t *testing.T) {
inst, _ := aetest.NewInstance(nil) //ignoring error for brevity
defer inst.Close()
req, _ := inst.NewRequest("GET", "/user/john#example.com/id-123", nil)
req.Header.Add("X-Requested-With", "XMLHttpRequest")
rec := httptest.NewRecorder()
r := makeRouter()
r.ServeHTTP(rec, req)
if email := rec.Body.String(); email != "john#example.com" {
t.Errorf("router failed, expected: %s, got: %s", "john#example.com", email)
}
}
Notice I removed the rest routes since that's not part of your test, but the idea would be the same. Also didn't check for errors for simplicity.