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.71k stars 8.01k forks source link

How to unit test properly #1195

Open jayhuang75 opened 6 years ago

jayhuang75 commented 6 years ago

Hello All,

I'm seeking the best way to unit testing my handler:

In main file: ` func init() { db.Connect() }

func main() { // Init API api := gin.Default()

// Using Middle-wares get the db session in the context for each request
api.Use(middlewares.Connect)

    routerProjects := api.Group("/projects")
{
    // All Projects
    routerProjects.GET("", projects.ListAllProjects)
    }

`

Here is my handler function ` func ListAllProjects(c *gin.Context) {

session := c.MustGet("session").(*mgo.Session)
db := session.DB("myDB")

var projects []models.ProjectList
err := db.C("projects").Find(bson.M{}).All(&projects)

if err != nil {
    helpers.RespondWithError(400, err.Error(), c)
}

c.JSON(200, projects)

} `

Please advise. Thank you

thinkerou commented 6 years ago

@jayhuang75 please see https://github.com/gin-gonic/gin#testing

kishankishore29 commented 2 years ago

@jayhuang75 please see https://github.com/gin-gonic/gin#testing

Whatever has been explained in the example seems to be more of an integration test rather than an unit test if I understand correctly?

jspc commented 2 years ago

@kishankishore29 if your business logic lives within your handler, then you can't really do anything else. Consider:

package main

type Server struct {
    router *gin.Engine
    db     Database
    smtp   SMTP
}

//snip

func (s Server) Signup(g *gin.Context) {
    u := new(User)

    err := g.BindJSON(u)
    if err != nil {
        g.AbortWithStatusJSON(400, s.errorMsg("invalid input"))

        return
    }

    err = db.CreateUser()
    if err != nil {
        g.AbortWithStatusJSON(500, s.errorMsg("database error"))

        return
    }

    g.JSON(204, s.successMsg("user created!"))

    err = smtp.SendWelcomeEmail(u.Email)
    if err != nil {
        log.Print(err)
    }
}

In this example, you can only really run an integration test. However, if you move that business logic out of the handler, and have the handler call that function, you can test however you want.

kishankishore29 commented 2 years ago

@kishankishore29 if your business logic lives within your handler, then you can't really do anything else. Consider:

package main

type Server struct {
  router *gin.Engine
  db     Database
  smtp   SMTP
}

//snip

func (s Server) Signup(g *gin.Context) {
  u := new(User)

  err := g.BindJSON(u)
  if err != nil {
      g.AbortWithStatusJSON(400, s.errorMsg("invalid input"))

      return
  }

  err = db.CreateUser()
  if err != nil {
      g.AbortWithStatusJSON(500, s.errorMsg("database error"))

      return
  }

  g.JSON(204, s.successMsg("user created!"))

  err = smtp.SendWelcomeEmail(u.Email)
  if err != nil {
      log.Print(err)
  }
}

In this example, you can only really run an integration test. However, if you move that business logic out of the handler, and have the handler call that function, you can test however you want.

Thanks for the response. I am working on a similar use case and have tried to implement an unit test: https://github.com/kishankishore29/person-service/pull/5/files . But I totally agree with your argument of dependency inversion.