In my handler tests, I use the pattern of serving a test request with an authentication token in the header a large number of times. To abstract this, and save myself a large number of lines, I've written the following function:
func serveTestReq(payload string, route string, method string, handlerfunc func(w http.ResponseWriter, r *http.Request), token string) {
body := strings.NewReader(payload)
req, err := http.NewRequest(method, route, body)
Expect(err).NotTo(HaveOccurred())
req.Header.Add("Content", "application/json")
req.Header.Add("Authorization", "Bearer "+token)
handler := authMiddleware(handlerfunc)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
}
However if I call this function twice (to test for idempotent POSTs, for example) the request seems to only be served once. Is there something wrong with the above function?
The problem was that I wasn't checking for the HTTP Response generated in the function. The correct function looks like:
func serveTestReq(payload string, route string, method string, handlerfunc func(w http.ResponseWriter, r *http.Request), token string) *httptest.RepsonseRecorder {
body := strings.NewReader(payload)
req, err := http.NewRequest(method, route, body)
Expect(err).NotTo(HaveOccurred())
req.Header.Add("Content", "application/json")
req.Header.Add("Authorization", "Bearer "+token)
handler := authMiddleware(handlerfunc)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
return rr
}
Related
can anyone help me im trying to create function unit testing but i cant get pass jwt auhtentication because fiber app.test didnt pass any jwt value from authorization header
i can get data if im using param but i cant use bearer if i use that
my code :
app := fiber.New()
urlvalue, _ := url.Parse("/test")
values := urlvalue.Query()
//values.Add("id", strconv.Itoa(2))
urlvalue.RawQuery = values.Encode()
log.Println(urlvalue.String())
app.Get("/test", func(c *fiber.Ctx) error {
return GetProject(c)
})
req := httptest.NewRequest("GET", urlvalue.String(), nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", jwt))
r, err := app.Test(req)
log.Println("Request : ", req)
log.Println(r)
assert.Nil(t, err, "There is error")
body, _ := ioutil.ReadAll(r.Body)
log.Println(string(body))
//err := json.NewDecoder(r.Body).Decode(&data)
assert.NotNil(t, req, "No value passed ")
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 ~
For sentence
resp, err := client.Get(fmt.Sprintf("https://www.xxxxx/day?time=%s", time))
If I want to mock a response to this client.Get() in unit test, I should use httptest.server, but how can I bind the url (https://www.xxxxx/day?time=%s) to the url of httptest.server? so that when I call client.Get() it can return the response I set before.
For some reason I cannot mock a client here.
You don't, usually. You take the base URL from the server and give it to the client:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestClient(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify request, send mock response, etc.
}))
defer server.Close()
var client *http.Client
var time time.Time
baseURL := server.URL // Something like "http://127.0.0.1:53791"
resp, err := client.Get(fmt.Sprintf(baseURL+"/day?time=%s", time))
if err != nil {
t.Fatal(err)
}
// Verify response body if applicable
resp.Body.Close()
}
Like this
func NewTestServerWithURL(URL string, handler http.Handler) (*httptest.Server, error) {
ts := httptest.NewUnstartedServer(handler)
if URL != "" {
l, err := net.Listen("tcp", URL)
if err != nil {
return nil, err
}
ts.Listener.Close()
ts.Listener = l
}
ts.Start()
return ts, nil
}
The http.Client is a struct not an interface which makes mocking it difficult as you have seen. An alternative way of mocking it is passing in the external dependencies that a routine needs, so instead of directly using client.Get, you use clientGet - which is a function pointer that was handed into the routine.
From the unit test you can then create :
mockClientGet(c *http.client, url string) (resp *http.Response, err error) {
// add the test code to return what you want it to.
}
Then in your main code use:
resp, err := clientGet(client, fmt.Sprintf("https://www.xxxxx/day?time=%s", time))
When calling the procedure normally, use the function pointer to http.Client.Get, and for your test pass in a pointer to your mock. It's not ideal, but I've not seen a nicer way around mocking non-interface external calls - and given its an external dependency, injecting it from the outside is not a bad thing.
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.
I am trying to add form variables to a Go http request.
Here's how my Go test looks:
func sample_test(t *testing.T) {
handler := &my_listener_class{}
reader := strings.NewReader("number=2")
req, _ := http.NewRequest("POST", "/my_url", reader)
w := httptest.NewRecorder()
handler.function_to_test(w, req)
if w.Code != http.StatusOK {
t.Errorf("Home page didn't return %v", http.StatusOK)
}
}
The issue is that the form data never gets passed on to the function I need to test.
The other relevant function is:
func (listener *my_listener_class) function_to_test(writer http.ResponseWriter, request *http.Request) {
...
}
I am using Go version go1.3.3 darwin/amd64.
You need to add a Content-Type header to the request so the handler will know how to treat the POST body data:
req, _ := http.NewRequest("POST", "/my_url", reader) //BTW check for error
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")