gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
78.44k stars 8.01k forks source link

gin.CreateTestContext() may happen that the status code unexpected. #3569

Open baytan0720 opened 1 year ago

baytan0720 commented 1 year ago

Description

I am testing the correctness of my code. There is a handler called func XXXHandler(c *gin.Context) with simple internal logic that calls c.Status(204) at the end. When I use gin.CreateTestContext(httptest.NewRecorder()) to create a ctx and pass it into the handler, the value of recorder.Code after the call does not meet my expectations. I then proceeded to test other status codes and confirmed that the c.Status() function is not working correctly in this environment.

How to reproduce

package main

import (
    "fmt"
    "net/http/httptest"

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

func handler(c *gin.Context) {
    c.Status(204)
}

func TestHandler() {
    rec := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(rec)
    handler(c)
    fmt.Println(rec.Code)
}

func main() {
    TestHandler()
}

Expectations

204

Actual result

200

Environment

baytan0720 commented 1 year ago

I think I have a good understanding of what caused the error. Normally, in the gin.go file at line 621, Gin would call c.Writer.WriteHeaderNow() once. When calling methods like c.JSON, c.String and so on, which all call c.Writer.Write(), they would first call c.Writer.WriteHeaderNow(). In this case, there were at least two calls.This is why only c.Status() did not work.

This is my first time participating in an open-source project, and I'm very eager to contribute to gin. Doing so would give me the confidence to continue working on open-source projects. If possible, could this issue be assigned to me?

araujo88 commented 1 year ago

I believe that the handler is functioning as intended. Instead retrieving the value of rec.Code, which hasn't been updated in the call to your handler function, you should get c.Writer.Status(), which will return 204, the expected status code.

With that considerations, the updated function:

func TestHandler() {
    rec := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(rec)
    handler(c)
    fmt.Println(c.Writer.Status()) // updated line
}

Outputs 204.

Note that your handler function does not update the object httptest.NewRecorder() with the new status code, only the context.