Gin: Wrong status code returned when using httptest.NewRecorder

Created on 28 Sep 2017  路  6Comments  路  Source: gin-gonic/gin

When testing a gin handler using a httptest.NewRecorder, when using (inside the handler) c.Status(404) to set the status code, and then bailing out of the request, then the status from the recorder is wrongly returned as 200.

On the other hand if we use c.JSON(404, gin.H{}) then the status code inside the recorder is correct.

The expected behavior is of course to always receive in the recorder the status that was set inside the handler.

Example code reproducing the bug bellow, both tests are expected to PASS but only the one using JSON does.

Having a main.go with the following code:

package main

import (
    "net/http"
    "os"

    "github.com/gin-gonic/gin"
)

func main() {
    port := ":" + os.Getenv("PORT")

    r := gin.Default()
    r.GET("/fail1", failStatus)
    r.GET("/fail2", failJson)
    r.Run(port)
}

func failStatus(c *gin.Context) {
    c.Status(http.StatusNotFound)
}

func failJson(c *gin.Context) {
    c.JSON(http.StatusNotFound, gin.H{"error": true})
}

And a main_test.go:

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/gin-gonic/gin"
)

func TestFailStatus(t *testing.T) {
    gin.SetMode(gin.TestMode)

    rec := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(rec)
    c.Request, _ = http.NewRequest("GET", "/fail1", nil)

    failStatus(c)

    res := rec.Result()
    if res.StatusCode != http.StatusNotFound { // Should be 404
        t.Fatalf("Expecting status to be 404 got %v", res.StatusCode)
    }
}

func TestFailJson(t *testing.T) {
    gin.SetMode(gin.TestMode)

    rec := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(rec)
    c.Request, _ = http.NewRequest("GET", "/fail1", nil)

    failJson(c)

    res := rec.Result()
    if res.StatusCode != http.StatusNotFound { // Should be 404
        t.Fatalf("Expecting status to be 404 got %v", res.StatusCode)
    }
}

Most helpful comment

Bumping as error is still reproducible and discussion has been silent. :smile: Any update on the behavior of c.JSON vs c.Status not performing consistently?

All 6 comments

Because testing handler didn't through gin.Engine(router). You can use the following example:

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/gin-gonic/gin"
)


func TestFailStatus(t *testing.T) {
    gin.SetMode(gin.TestMode)

    router := gin.New()

    res:=performRequest("GET","/fail1",router)

    if res.Code != http.StatusNotFound { // Should be 404
        t.Fatalf("Expecting status to be 404 got %v", res.Code)
    }
}

func TestFailJson(t *testing.T) {
    gin.SetMode(gin.TestMode)

    router := gin.New()

    res:=performRequest("GET","/fail2",router)

    if res.Code != http.StatusNotFound { // Should be 404
        t.Fatalf("Expecting status to be 404 got %v", res.Code)
    }
}

func performRequest(method, target string, router *gin.Engine) *httptest.ResponseRecorder {
    r := httptest.NewRequest(method, target, nil)
    w := httptest.NewRecorder()
    router.ServeHTTP(w, r)
    return w
}

Hi @easonlin404, thanks for your answer.

Correct me if I'm wrong, but in this case I would be testing the handler and the engine and not only the handler.

In a real project I would have a struct that it's instantiated by injecting dependencies (when testing mocks) and then the a Handler that is called. What I want to test is the logic of my handler, and not the routing/engine/middlewares/handler combination (that would be another kind of test I think).

Either way, the thing that I consider a bug is that c.Status does not behave the same way as c.JSON or c.String in a testing context. The expected behavior would be for all methods of context that assign a status code on the response to work the same way and make the status visible on the httptest.NewRecorder instance.

I got the same issue error in testing context.

  • c.Status(201) cannot set the status
  • c.JSON(201,...) setting status ok

Bumping as error is still reproducible and discussion has been silent. :smile: Any update on the behavior of c.JSON vs c.Status not performing consistently?

This bug is still present. Setting c.Status(204) does not result in the responserecorder's code being updated. My workaround at the moment is just getting the status from c.Writer.Status() in tests.

This bug is still present. Setting c.Status(204) does not result in the responserecorder's code being updated. My workaround at the moment is just getting the status from c.Writer.Status() in tests.

This, I think, is the best workaround available.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mastrolinux picture mastrolinux  路  3Comments

rawoke083 picture rawoke083  路  3Comments

olegsobchuk picture olegsobchuk  路  3Comments

oryband picture oryband  路  3Comments

ccaza picture ccaza  路  3Comments