在Go语言中进行接口模拟的方法

huangapple go评论83阅读模式
英文:

methods for interface mocking in go

问题

如果我有一个像下面这样的handlerfunc,那么在测试中“模拟”或注入一个包装某个对象的接口的最佳方法是什么?

func GetOrCreateUser(w http.ResponseWriter, r *http.Request) {
    user := GetUserFromContext(r.Context())
    if created := user.GetOrCreate(); created {
        smtp.SendEmail(...)
        return
    } else {
        w.Write([]byte("Hello User!"))
    }
}

我目前找到的唯一方法似乎是这样做:

type Mailer interface {
    SendMail()
}

func HandlerWithMailer(m Mailer) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user := GetUserFromContext(r.Context())
        if created := user.GetOrCreate(); created {
            m.SendEmail(...)
            return
        } else {
            w.Write([]byte("Hello User!"))
        }
    }
}

然后在路由器中这样调用邮件发送器(使用httprouter):

m := mailer.New() // 假设这会返回一个新的邮件发送器
r := httprouter.New()
r.GET("/mailer", HandlerWithMailer(m))

然后可以通过创建一个实现该接口并返回任何我想要的结构体来进行测试,测试中可以调用它。我知道这种方法是可行的,但我想知道这是否是Go中首选的测试方法,以及是否有其他方法可以实现这一目标。

英文:

If I have a handlerfunc like the one below. What is the best way to "mock" or inject a interface that wraps some object for testing?

func GetOrCreateUser(w http.ResponseWriter, r *http.Request) {
    user := GetUserFromContext(r.Context())
    if created :=user.GetOrCreate(); created {
        smtp.SendEmail(...)
        return
    } else {
        w.Write([]byte("Hello User!"))
    }
}

The only way that I have come by seems to be to do this:

type Mailer interface { SendMail() }

func HandlerWithMailer(m Mailer) http.handlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user := GetUserFromContext(r.Context())
        if created := user.GetOrCreate(); created {
            m.SendEmail(...)
            return
        } else { 				
            w.Write([]byte("Hello User!"))
        }
    }
}

Then calling the mailer like this in the router (using httprouter):

m := mailer.New() // assuming this returns a new mailer
r := httprouter.New()
r.GET("/mailer", HandlerWithMailer(m))

Which could then be tested by making a struct that implements the interface and returns whatever I want, which can be called in the tests. I know this works, but I am wondering if this is the preferred method of testing in Go and if there is any other way to go about accomplishing this.

答案1

得分: 1

我会像这样从一个结构体中调用我的处理程序:

type Handlers struct {
    mailer *Mailer
}

func (h *Handlers) GetOrCreateUser(w http.ResponseWriter, r *http.Request) {
    user := GetUserFromContext(r.Context())
    if created := user.GetOrCreate(); created {
        h.mailer.SendEmail(...)
        return
    } else {
        w.Write([]byte("Hello User!"))
    }
}

这样,你可以使用你想要使用的Mailer实现来实例化结构体。

m := mailer.New() // 假设这返回一个新的 mailer
h := Handlers{&m}

r := httprouter.New()
r.GET("/mailer", h.HandlerWithMailer)

在你的测试中:

m := mockMailer.New() // 假设这返回一个新的 mailer
h := Handlers{&m}

r := httprouter.New()
r.GET("/mailer", h.HandlerWithMailer)
英文:

I would call my handlers from a struct like so:

type Handlers struct {
	mailer *Mailer
}

func(h *Handlers) GetOrCreateUser(w http.ResponseWriter, r *http.Request) {
    user := GetUserFromContext(r.Context())
    if created :=user.GetOrCreate(); created {
        h.mailer.SendEmail(...)
        return
    } else {
        w.Write([]byte("Hello User!"))
    }
}

This way you can instansiate the struct with the implementation of Mailer you want to use.

m := mailer.New() // assuming this returns a new mailer
h := Handlers{&m}

r := httprouter.New()
r.GET("/mailer", h.HandlerWithMailer)

and in your tests

m := mockMailer.New() // assuming this returns a new mailer
h := Handlers{&m}
    
r := httprouter.New()
r.GET("/mailer", h.HandlerWithMailer)

huangapple
  • 本文由 发表于 2017年3月14日 07:33:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/42775292.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定