Unit Testing With Gin-Gonic
You cannot modify a method of a type in Go. It is defined and immuatable by the package that defines the type at compile time. This is a design decision by Go. Simply don't do it.
You have already use httptest.NewRecorder()
as a mock of gin.Context.ResponseWriter
, which will records what is written to the response, including the c.JSON
call. However, you need to keep a reference of the httptest.ReponseRecorder
and then check it later. Note that you only have a marshalled JSON, so you need to unmarshal it to check content (as both Go map and JSON objects's order does not matter, checking marshalled string's equality is error-prone).
For example,
func TestGetResourceById(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
GetResourceById(c)
assert.Equal(t, 200, w.Code) // or what value you need it to be
var got gin.H
err := json.Unmarshal(w.Body.Bytes(), &got)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, want, got) // want is a gin.H that contains the wanted map.
}
Evan Bloemer
Updated on June 23, 2022Comments
-
Evan Bloemer almost 2 years
My project is split into three main components: controllers, services, and models. When a route is queried via the URI, the controllers are called, which then call the services to interact with the models, which then interact with the database via gorm.
I am trying to write unit tests for the controllers, but I'm having a hard time understanding how to properly mock the services layer while mocking the gin layer. I can get a mocked gin context, but I'm not able to mock the service layer within my controller method. Below is my code:
resourceController.go
package controllers import ( "MyApi/models" "MyApi/services" "github.com/gin-gonic/gin" "net/http" ) func GetResourceById(c *gin.Context) { id := c.Param("id") resource, err := services.GetResourceById(id) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"status": http.StatusBadRequest, "message": err}) return } else if resource.ID == 0 { c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "Resource with id:"+id+" does not exist"}) return } c.JSON(http.StatusOK, gin.H{ "id": resource.ID, "data1": resource.Data1, "data2": resource.Data2, }) }
I want to test that the c.JSON is returning with the proper
http
status and other data. I need to mock theid
variable,err
variable, andc.JSON
function, but when I try to set thec.JSON
function in the test to my new function, I get an error sayingCannot assign to c.JSON
. Below is my attempt at writing a test:resourceController_test.go
package controllers import ( "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "net/http/httptest" "testing" ) func TestGetResourceById(t *testing.T) { var status int var body interface{} c, _ := gin.CreateTestContext(httptest.NewRecorder()) c.JSON = func(stat int, object interface{}) { status = stat body = object } GetResourceById(c) assert.Equal(t, 4, 4) }
How do I properly write a unit test to test whether the c.JSON is returning the proper values?