在Golang的handler函数中如何注入client
在Golang的handler函数中如何注入client 我正在测试一个场景,希望在"CreateEmployee"函数中注入客户端,仅用于测试用例而不影响生产/预发布环境。
我的处理函数如下所示…
// 在系统中创建员工
func CreateEmployee(w http.ResponseWriter, r *http.Request) {
/// 此处有一些代码,将请求体中的JSON映射到HTTP请求...等等
response, e := client.Do(request) // 这是http.Client
}
我正在编写一个测试用例,如下所示
func TestCreateEmployee(t *testing.T) {
var jsonStr = []byte(`{员工详情JSON}`)
req, err := http.NewRequest("POST", "http://www.exapmle.com/empsys/", bytes.NewBuffer(jsonStr))
resp := httptest.NewRecorder()
CreateEmployee(resp,req) // 我在此处调用我的函数,但不确定client.Do会如何表现..
/// 以及后续代码
}
有人能建议测试这种情况的好方法吗?
更多关于在Golang的handler函数中如何注入client的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我该怎么办……我毫无头绪。是否可以硬编码一些响应……或者应该修改现有代码,使其具备可测试性
更多关于在Golang的handler函数中如何注入client的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我认为这个存根的想法对我来说可行……我会尝试一下……你知道有什么在线的存根示例吗?
在这个函数中,我对JSON对象(作为请求体接收)进行编组和解组,并将请求转发给第三方
那么当你调用 client.Do 时就必须捕获这种情况。但由于成功或失败并非由你掌控,你的测试将不具备确定性。这不是一个好主意。
func main() {
fmt.Println("hello world")
}
client 正在执行的 request 是什么?
这看起来像是一个集成测试,因为它依赖于第三方系统,即 client 连接的主机。该主机是否始终可用?
您是否想要隐藏在此注释后面的代码?
// some code here, mapping json from request body to http request...etc
请求示例是创建一个 Keycloak 用户,但 Keycloak 并非始终可用。
当我调用 client.Do(request) 时… 它会创建用户并返回一些响应
我不需要代码… 但每次调用 client.Do(request) 时总是遇到错误,所以想要模拟 Do 方法
client.Do(request)
存根代码会像这样吗
func main() {
mock := &ClientMock{}
err := TestMyClient(mock)
if err != nil {
fmt.Println(err.Error())
}
}
type HttpClient interface {
Do(req *http.Request) (*http.Response, error)
}
func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
return &http.Response{}, nil
}
type ClientMock struct {
}
你想要做什么?测试依赖于第三方服务的代码不是一个好主意。
你可以为第三方服务编写一个桩服务器:找出它提供的HTTP API的相关部分,以及你将调用client.Do的部分。实现一个HTTP服务,该服务对这些API部分进行桩模拟,并根据已知请求返回预定义的数据。然后编写测试,使client.Do通过这些请求调用桩服务器,并测试你的代码是否正确处理了响应。
在Golang中测试handler函数时,为了控制HTTP客户端的行为,可以使用依赖注入模式。以下是几种实现方法:
方法1:使用接口包装http.Client
首先定义一个HTTP客户端接口:
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
type CustomClient struct {
Client HTTPClient
}
// 在生产环境中使用真实的http.Client
func NewCustomClient() *CustomClient {
return &CustomClient{
Client: &http.Client{},
}
}
// 在handler中通过参数传入
func CreateEmployee(w http.ResponseWriter, r *http.Request, client HTTPClient) {
// 原有代码...
response, e := client.Do(request)
// 处理响应...
}
方法2:使用全局变量和测试替换
var (
httpClient HTTPClient = &http.Client{}
)
func CreateEmployee(w http.ResponseWriter, r *http.Request) {
// 使用全局的httpClient
response, e := httpClient.Do(request)
// 处理响应...
}
// 在测试中替换httpClient
func TestCreateEmployee(t *testing.T) {
// 保存原始客户端
originalClient := httpClient
defer func() { httpClient = originalClient }()
// 使用模拟客户端
httpClient = &mockHTTPClient{}
// 执行测试
var jsonStr = []byte(`{"name":"John","age":30}`)
req, _ := http.NewRequest("POST", "/employees", bytes.NewBuffer(jsonStr))
resp := httptest.NewRecorder()
CreateEmployee(resp, req)
// 验证结果...
}
方法3:使用结构体和依赖注入
type EmployeeHandler struct {
Client HTTPClient
}
func NewEmployeeHandler(client HTTPClient) *EmployeeHandler {
if client == nil {
client = &http.Client{}
}
return &EmployeeHandler{Client: client}
}
func (h *EmployeeHandler) CreateEmployee(w http.ResponseWriter, r *http.Request) {
response, e := h.Client.Do(request)
// 处理响应...
}
// 测试代码
func TestCreateEmployee(t *testing.T) {
// 创建模拟客户端
mockClient := &mockHTTPClient{}
handler := NewEmployeeHandler(mockClient)
var jsonStr = []byte(`{"name":"John","age":30}`)
req, _ := http.NewRequest("POST", "/employees", bytes.NewBuffer(jsonStr))
resp := httptest.NewRecorder()
handler.CreateEmployee(resp, req)
// 验证结果...
}
模拟HTTP客户端实现
type mockHTTPClient struct{}
func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
// 返回模拟响应
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBufferString(`{"id":123,"status":"success"}`)),
Header: make(http.Header),
}, nil
}
方法4:使用函数类型
type DoFunc func(*http.Request) (*http.Response, error)
func CreateEmployee(w http.ResponseWriter, r *http.Request, doFunc DoFunc) {
if doFunc == nil {
doFunc = http.DefaultClient.Do
}
response, e := doFunc(request)
// 处理响应...
}
// 测试中使用
func TestCreateEmployee(t *testing.T) {
mockDo := func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusCreated,
Body: io.NopCloser(bytes.NewBufferString(`{"id":1}`)),
}, nil
}
var jsonStr = []byte(`{"name":"John"}`)
req, _ := http.NewRequest("POST", "/employees", bytes.NewBuffer(jsonStr))
resp := httptest.NewRecorder()
CreateEmployee(resp, req, mockDo)
}
推荐使用方法3(结构体依赖注入),因为它提供了最好的可测试性和清晰的关注点分离。在生产代码中,你可以使用真实的HTTP客户端,在测试中注入模拟实现。


