如何在Golang测试中模拟一个进行外部调用的中间件?
How to mock a middleware that makes external call in Golang test?
TLDR: 有一个 go-chi
中间件正在进行外部调用以授权请求。我需要模拟它的作用。
详情:
我有这个测试:
func Test_HTTP_UserSet_Create_InvalidAction(t *testing.T) {
requestBody :=
`{
"name": "test",
"action": "TEST"
}`
req, _ := http.NewRequest("POST", "/1234/users", bytes.NewBuffer([]byte(requestBody)))
recorder := setupInvalidActionRecorder(t, req)
if status := recorder.Code; status != http.StatusBadRequest {
t.Errorf("Status code expected to be %d but got %d", http.StatusBadRequest, status)
return
}
}
上面的setupUnknownErrorRecorder
是:
func setupUnknownErrorRecorder(t *testing.T, request *http.Request) *httptest.ResponseRecorder {
recorder := httptest.NewRecorder()
c := resty.New()
resource := users.NewResource(
httpClient.NewHttpClient(log, &config.Server{}, &logger.LogRecord{}, c),
)
resource.Routes().ServeHTTP(recorder, request)
return recorder
}
我得到的结果是:
=== RUN Test_HTTP_UserSet_Create_InvalidAction
invalidAction_test.go:71: Status code expected to be 400 but got 401
--- FAIL: Test_HTTP_UserSet_Create_InvalidAction (0.00s)
这个结果是预期的,因为服务器正在使用我传递给 NewResource
的客户端 c
进行外部调用。
有一个 go-chi
中间件正在使用此客户端进行外部调用。
我的问题是:我怎样才能使这个 http 调用总是 return 200 而不是真正进行调用。又名如何模拟这个电话?
编辑:
上面httpClient的接口是:
type HttpClient struct {
logger *logger.Logger
config *config.Server
instrumentation *instrumentation
record *logger.LogRecord
restyClient *resty.Client
}
func NewHttpClient(
logger *logger.Logger,
config *config.Server,
record *logger.LogRecord,
restyClient *resty.Client,
) HttpClient {
return HttpClient{
instrumentation: newInstrumentation(logger, record),
config: config,
logger: logger,
record: record,
restyClient: restyClient,
}
}
可测试代码的一个非常基本的规则是,您不能只在其他服务中创建新服务。您需要注入它,如果 none 已经存在,则需要为 resty.New
返回的任何内容准备一个接口。这样你就可以在测试中注入你的模拟。
然后您可以使用 https://pkg.go.dev/github.com/stretchr/testify/mock 来生成模拟并说明模拟服务的返回值应该是什么。
评论后更新:
type HttpClient struct {
logger *logger.Logger
config *config.Server
instrumentation *instrumentation
record *logger.LogRecord
restyClient *resty.Client // <- this is the thing you want to change from a pointer to resty.Client to an interface
}
检查您在代码中使用了 restyClient
的哪些方法,并创建包含它们的新接口,例如:
type MyRestyClient interface {
FirstUsedMethod(..)
...
}
并将 restyClient 声明交换为
type HttpClient struct {
...
restyClient MyRestyClient
之后,您可以使用我之前粘贴的 link 中的嘲弄命令为您生成模拟。
稍后,您只需在测试中设置模拟:
restyMock := new(RestyMock)
restyMock.On("MethodYouExpect").Return(returnValueOfYourChoice)
然后您将准备好的模拟注入到您正在测试的服务中。
TLDR: 有一个 go-chi
中间件正在进行外部调用以授权请求。我需要模拟它的作用。
详情:
我有这个测试:
func Test_HTTP_UserSet_Create_InvalidAction(t *testing.T) {
requestBody :=
`{
"name": "test",
"action": "TEST"
}`
req, _ := http.NewRequest("POST", "/1234/users", bytes.NewBuffer([]byte(requestBody)))
recorder := setupInvalidActionRecorder(t, req)
if status := recorder.Code; status != http.StatusBadRequest {
t.Errorf("Status code expected to be %d but got %d", http.StatusBadRequest, status)
return
}
}
上面的setupUnknownErrorRecorder
是:
func setupUnknownErrorRecorder(t *testing.T, request *http.Request) *httptest.ResponseRecorder {
recorder := httptest.NewRecorder()
c := resty.New()
resource := users.NewResource(
httpClient.NewHttpClient(log, &config.Server{}, &logger.LogRecord{}, c),
)
resource.Routes().ServeHTTP(recorder, request)
return recorder
}
我得到的结果是:
=== RUN Test_HTTP_UserSet_Create_InvalidAction
invalidAction_test.go:71: Status code expected to be 400 but got 401
--- FAIL: Test_HTTP_UserSet_Create_InvalidAction (0.00s)
这个结果是预期的,因为服务器正在使用我传递给 NewResource
的客户端 c
进行外部调用。
有一个 go-chi
中间件正在使用此客户端进行外部调用。
我的问题是:我怎样才能使这个 http 调用总是 return 200 而不是真正进行调用。又名如何模拟这个电话?
编辑: 上面httpClient的接口是:
type HttpClient struct {
logger *logger.Logger
config *config.Server
instrumentation *instrumentation
record *logger.LogRecord
restyClient *resty.Client
}
func NewHttpClient(
logger *logger.Logger,
config *config.Server,
record *logger.LogRecord,
restyClient *resty.Client,
) HttpClient {
return HttpClient{
instrumentation: newInstrumentation(logger, record),
config: config,
logger: logger,
record: record,
restyClient: restyClient,
}
}
可测试代码的一个非常基本的规则是,您不能只在其他服务中创建新服务。您需要注入它,如果 none 已经存在,则需要为 resty.New
返回的任何内容准备一个接口。这样你就可以在测试中注入你的模拟。
然后您可以使用 https://pkg.go.dev/github.com/stretchr/testify/mock 来生成模拟并说明模拟服务的返回值应该是什么。
评论后更新:
type HttpClient struct {
logger *logger.Logger
config *config.Server
instrumentation *instrumentation
record *logger.LogRecord
restyClient *resty.Client // <- this is the thing you want to change from a pointer to resty.Client to an interface
}
检查您在代码中使用了 restyClient
的哪些方法,并创建包含它们的新接口,例如:
type MyRestyClient interface {
FirstUsedMethod(..)
...
}
并将 restyClient 声明交换为
type HttpClient struct {
...
restyClient MyRestyClient
之后,您可以使用我之前粘贴的 link 中的嘲弄命令为您生成模拟。
稍后,您只需在测试中设置模拟:
restyMock := new(RestyMock)
restyMock.On("MethodYouExpect").Return(returnValueOfYourChoice)
然后您将准备好的模拟注入到您正在测试的服务中。