简体   繁体   English

"测试在 Golang 的处理程序中调用该方法"

[英]Test that method is called in handler in Golang

I am implementing a API in Golang.我正在用 Golang 实现一个 API。 I have a endpoint where I am calling a method with parameters of other package.我有一个端点,我正在使用其他包的参数调用一个方法。 Now I need to check that, that method has been called in the request.现在我需要检查一下,该方法已在请求中调用。

Below is the small similar scenario what I am doing and what I am expecting.下面是我正在做的和我期待的类似的小场景。

My handler我的处理程序

package myPackage
import (
    "log"
    "github.com/myrepo/notifier" // my another package
)

func MyHandler(writer http.ResponseWriter, request *http.Request) { 
    // ...
    // ...

    notifier.Notify(4, "sdfsdf")

    // ...
    // ...

}

Testing handler测试处理程序

func TestMyHandler(t *testing.T) {
    // Here I want to 
    req, _ := http.NewRequest("GET", "/myendpoint", nil)
    // ... Want to test that notifier.Notify is called
    // ...
}

In the TestMyHandler, I want to check that notifier.Notify has called.在 TestMyHandler 中,我想检查notifier.Notify是否已调用。

Findings发现

I tried to understand the AssertNumberOfCalls , func (*Mock) Called , and func (*Mock) MethodCalled but I am not sure how to use them :(.我试图理解AssertNumberOfCallsfunc (*Mock) Calledfunc (*Mock) MethodCalled但我不知道如何使用它们:(。

I am a newbie in Golang and really exicted to do that.我是 Golang 的新手,真的很高兴能做到这一点。 Please let me know if I missed anything or you may need more information for more understaing.如果我遗漏了任何内容,请告诉我,否则您可能需要更多信息以了解更多信息。

Want to test that notifier.Notify is called. 要测试那个notifier.Notify被调用。

No you don't. 不,你不会。 You are interested in that the handler does what it should do and this seems to consist of two things: 您对处理程序做了应做的事情感兴趣,这似乎由两件事组成:

  1. Return the right response (easy to test with a net/http/httptest.ResponseRecorder), and 返回正确的响应(易于使用net / http / httptest.ResponseRecorder进行测试),然后

  2. Has some noticeable side effect, here issue some notification. 有一些明显的副作用,在这里发出一些通知。

To test 2. you test that the notification was issued, not that some function was called. 要测试2.您测试发出了通知,而不是调用了某些函数。 Whatever notify.Notify results in (eg a database entry, a file, some HTTP call) should be tested. 无论notify.Notify结果如何(例如数据库条目,文件,某些HTTP调用),都应进行测试。 Formally this is no longer unit testing but testing for side effects is never strict unit testing. 正式地,这不再是单元测试,但是对副作用的测试绝不是严格的单元测试。

What you can do: Wrap your handler logic into some object and observe that objects state. 您可以做什么:将处理程序逻辑包装到某个对象中并观察该对象的状态。 Ugly. 丑陋。 Don't. 别。

This is a good opportunity to use dependency injection and interfaces. 这是使用依赖项注入和接口的好机会。

Namely, we need to extract the concept of a Notifier 即,我们需要提取Notifier的概念

(warning: code not tested directly) (警告:代码未直接测试)

type Notifier interface {
    Notify(int, string)() error
}

Now to avoid any confusion with the notifier library, use a local alias. 现在,为了避免与notifier库混淆,请使用本地别名。

import "github.com/myrepo/notifier" mynotifier

Then, because the library you're using exports it as a function, not within a struct, we'll need to make a struct that implements our interface 然后,由于您正在使用的库将其导出为函数而不是结构,因此我们需要创建一个实现我们接口的结构

type myNotifier struct {}

func (mn *myNotifier) Notify(n int, message string) error {
  return mynotifier.Notify(n, message)
}

Then you modify your function: 然后修改函数:

func MyHandler(writer http.ResponseWriter, request *http.Request, notifier Notifier) { 
    // ...
    // ...

    notifier.Notify(4, "sdfsdf")

    // ...
    // ...

}

Then in your test, you're now free to send in a spy Notifier 然后在测试中,您现在可以自由发送间谍通知程序

type spyNotifier struct {
  called boolean
}

func (n *spyNotifier) Notify(n int, msg string) error {
  n.called = true
  return
}

This approach is similar to Mathew's answer<\/a> , but uses the mock<\/a> package from testify<\/a> instead.这种方法类似于Mathew 的 answer<\/a> ,但使用testify<\/a>中的mock<\/a>包。 Here, you create a mock implementation of the Notifier, register the method call, and assert that the method has been called with the expected arguments.在这里,您创建 Notifier 的模拟实现,注册方法调用,并断言已使用预期参数调用了该方法。

Implementation执行<\/h1>
package handler import ( "net\/http" "github.com\/stretchr\/testify\/mock" ) \/\/ the interface for the Notifier type Notifier interface { Notify(int, string) error } \/\/ the mock implementation of the interface above type MockNotifier struct { mock.Mock } \/\/ stub the notify method to ensure it can be expected later in the test func (mockNotifier *MockNotifier) Notify(arg1 int, arg2 string) error { args := mockNotifier.Called(arg1, arg2) return args.Error(0) } \/\/ this handler which accepts a Notifier for dependency injection type Handler struct { notifier Notifier } \/\/ the MyHandler implementation which calls the notifier instance func (h *Handler) MyHandler(writer http.ResponseWriter, request *http.Request) { \/\/ this is what we want to test! h.notifier.Notify(4, "sdfsdf") }<\/code><\/pre>

Test测试<\/h1>
package handler_test import ( "net\/http" "net\/http\/httptest" "testing" ) func TestMyHandler(t *testing.T) { t.Parallel() mockNotifier := MockNotifier{} handler := Handler{ notifier: &mockNotifier } \/\/ register the mock to expect a call to Notify with the given arguments and return nil as the error mockNotifier.On("Notify", 4, "sdfsdf").Return(nil) \/\/ setup the test arguments request := httptest.NewRequest(http.MethodGet, "\/someapi", nil) writer := httptest.NewRecorder() \/\/ call the handler handler.MyHandler(writer, request) \/\/ this is the important part!! \/\/ this ensures that the mock Notify method was called with the correct arguments, otherwise the test will fail mockNotifier.AssertExpectations(t) }<\/code><\/pre>"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM