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")
Related
Below is my code for main.go
func main() {
app := fiber.New()
app.Use(recover.New())
inferenceController := controllers.InferenceController
middleware := middleware.Middleware
privateRoutes := routes.PrivateRoutes{InferenceController: inferenceController,Middleware: middleware }
privateRoutes.Routes(app)
log.Fatal(app.Listen(":3000"))
}
I am trying to test this code but can't figure out the way for testing
In your test you actually need to create the app and register the relevent handlers. Then use app.Test() to call the handler. You can create body content as needed and check response codes and body content.
In this model you setup your server with just the endpoints/middleware you need for each test case. You can provide mock's around this if you need, depending on your specific use case.
For your example above, it would be something like the below, not knowing what your actual endpoints are:
func TestMyFiberEndpoiunt(t *testing.T) {
// Setup the app
app := Fiber.New()
app.Use(recover.New())
inferenceController := controllers.InferenceController
middleware := middleware.Middleware
privateRoutes := routes.PrivateRoutes{InferenceController: inferenceController,Middleware: middleware }
privateRoutes.Routes(app)
// Setup your request body
reqBody := ReqData{ SomeData: "something" }
bodyJson, _ := json.Marshal(&reqBody)
req := httptest.NewRequest("GET", "/api/v1/endpoint", bytes.NewReader(bodyJson))
resp, _ := app.Test(req, 10)
// Check the expected response code
assert.Equal(t, 200, resp.StatusCode)
// Check the body content
respBody := make(byte, resp.ContentLength)
_, _ = resp.Body.read(respBody)
assert.Equal(t, `{"data":"expected"}`, string(respBody))
}
If you need stateful data accross multiple tests for some use case, you could setup your server in a TestMain with all the needed routes and share it as a package var.
If the data marshalling seems like a lot of overhead for each test case, you can use a helper function such as:
func GetJsonTestRequestResponse(app *fiber.App, method string, url string, reqBody any) (code int, respBody map[string]any, err error) {
bodyJson := []byte("")
if reqBody != nil {
bodyJson, _ := json.Marshal(reqBody)
}
req := httptest.NewRequest(method, url, bytes.NewReader(bodyJson))
resp, err := app.Test(req, 10)
code = resp.StatusCode
// If error we're done
if err != nil {
return
}
// If no body content, we're done
if resp.ContentLength == 0 {
return
}
bodyData := make([]byte, resp.ContentLength)
_, _ = resp.Body.Read(bodyData)
err = json.Unmarshal(bodyData, &respBody)
return
}
Then tests cases look cleaner and are easier to write (imho).
type testArg struct {
Arg1 string
Arg2 int
}
func TestMyFakeEndpoint(t *testing.T) {
app := fiber.New()
defer app.Shutdown()
app.Post("/test", func(c *fiber.Ctx) error {
arg := testArg{}
_ = json.Unmarshal(c.Request().Body(), &arg)
return c.JSON(arg)
})
code, body, err := GetJsonTestRequestResponse(app, "POST", "/test", testArg{"testing", 123})
assert.Nil(t, err)
assert.Equal(t, 200, code)
assert.EqualValues(t, body["Arg1"], "testing")
assert.EqualValues(t, body["Arg2"], 123)
}
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 ~
I've written http client wrapper in go and I need to test it thoroughly.
I'm reading the response body with ioutil.ReadAll within the wrapper. I'm having a bit of trouble figuring out how I can force a read from the response body to fail with the help of httptest.
package req
func GetContent(url string) ([]byte, error) {
response, err := httpClient.Get(url)
// some header validation goes here
body, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
errStr := fmt.Sprintf("Unable to read from body %s", err)
return nil, errors.New(errStr)
}
return body, nil
}
I'm assuming I can set up a fake server as such:
package req_test
func Test_GetContent_RequestBodyReadError(t *testing.T) {
handler := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
ts := httptest.NewServer(http.HandlerFunc(handler))
defer ts.Close()
_, err := GetContent(ts.URL)
if err != nil {
t.Log("Body read failed as expected.")
} else {
t.Fatalf("Method did not fail as expected")
}
}
I'm assuming I need to modify the ResposeWriter. Now, is there any way for me to modify the responseWriter and thereby force the ioutil.ReadAll in the wrapper to fail?
I realize that you seem to think it's a duplicate of this post and while you may believe so or it might be, just marking it as a duplicate doesn't really help me. The code provided in the answers in the "duplicate" post makes very little sense to me in this context.
Check the documentation of Response.Body to see when reading from it might return an error:
// Body represents the response body.
//
// The response body is streamed on demand as the Body field
// is read. If the network connection fails or the server
// terminates the response, Body.Read calls return an error.
//
// The http Client and Transport guarantee that Body is always
// non-nil, even on responses without a body or responses with
// a zero-length body. It is the caller's responsibility to
// close Body. The default HTTP client's Transport may not
// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
// not read to completion and closed.
//
// The Body is automatically dechunked if the server replied
// with a "chunked" Transfer-Encoding.
Body io.ReadCloser
The easiest way is to generate an invalid HTTP response from the test handler.
How to do that? There are many ways, a simple one is to "lie" about the content length:
handler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", "1")
}
This handler tells it has 1 byte body, but actually it sends none. So at the other end (the client) when attempting to read 1 byte from it, obviously that won't succeed, and will result in the following error:
Unable to read from body unexpected EOF
See related question if you would need to simulate error reading from a request body (not from a response body): How do I test an error on reading from a request body?
To expand upon icza's awesome answer, you can also do this with an httptest.Server object:
bodyErrorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", "1")
}))
defer bodyErrorServer.Close()
You can then pass bodyErrorServer.URL in your tests like normal, and you'll always get an EOF error:
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func getBodyFromURL(service string, clientTimeout int) (string, error) {
var netClient = &http.Client{
Timeout: time.Duration(clientTimeout) * time.Millisecond,
}
rsp, err := netClient.Get(service)
if err != nil {
return "", err
}
defer rsp.Body.Close()
if rsp.StatusCode != 200 {
return "", fmt.Errorf("HTTP request error. Response code: %d", rsp.StatusCode)
}
buf, err := ioutil.ReadAll(rsp.Body)
if err != nil {
return "", err
}
return string(bytes.TrimSpace(buf)), nil
}
func TestBodyError(t *testing.T) {
bodyErrorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", "1")
}))
_, err := getBodyFromURL(bodyErrorServer.URL, 1000)
if err.Error() != "unexpected EOF" {
t.Error("GOT AN ERROR")
} else if err == nil {
t.Error("GOT NO ERROR, THATS WRONG!")
} else {
t.Log("Got an unexpected EOF as expected, horray!")
}
}
Playground example here: https://play.golang.org/p/JzPmatibgZn
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
}
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.