Golang中如何模拟结构体方法进行测试
Golang中如何模拟结构体方法进行测试 我对在结构体方法中模拟HTTP请求有些困惑。给定以下示例,如何避免对真实服务器进行HTTP调用。
type Graph struct {
ClientID string
ClientSecret string
}
func (g *Graph) RefreshToken() err {
}
func (g *Graph) GetJSON(method string, url string) ([]byte, error) {
// 刷新令牌
g.RefreshToken()
request, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}
response, err := Client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return body, err
}
return body, nil
}
func (g *Graph) GetUsers() (map[string]interface{}, error) {
result, err := g.GetJSON("GET", "https://example.com/users")
}
// main.go
client := Graph{`clientId`, `clientsecret`}
users := client.GetUsers()
更多关于Golang中如何模拟结构体方法进行测试的实战教程也可以访问 https://www.itying.com/category-94-b0.html
hollowaykeanho:
GetJSONFx
谢谢 @hollowaykeanho。这是标准做法吗?
更多关于Golang中如何模拟结构体方法进行测试的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是一种常见做法,而非规则(标准)。
另一种策略取决于如何实现结构体初始化。对于这段代码片段,以下方式更有意义。该策略在使用 struct 之前,将 g.GetJSONFx 默认初始化为 getServerJSON,这样你就可以完全安全地跳过 GetJSON 内部的检查。
func main() {
fmt.Println("hello world")
}
这样可行吗(由于你的代码不完整,尚未测试)?
type Graph struct {
ClientID string
ClientSecret string
GetJSONFx func(method string, url string) ([]byte, error)
}
func (g *Graph) GetJSON(method string, url string) ([]byte, error) {
if g.GetJSONFx != nil {
return g.GetJSONFx(method, url)
}
return g.getServerJSON(method, url)
}
func (g *Graph) getServerJSON(method string, url string) ([]byte, error) {
// Refresh token
g.RefreshToken()
request, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}
response, err := Client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return body, err
}
return body, nil
}
func main() {
g := &Graph{
GetJSONFx = func(method string, url string) ([]byte, error) {
... // your custom function
},
}
g.GetJSON(...)
}
还有其他类似的方法,但由于你硬编码了服务器端代码,这种策略更有意义。
@BastinRobin,等等,我之前没注意到你只是用于测试。对于测试,请使用:
https://pkg.go.dev/net/http/httptest
你可以模拟请求和响应,并将其传入你的方法。
上述策略是针对运行时模拟(如应用程序模拟/变更)。除非你的应用程序正在使用它,否则请避免在测试中使用。
更新:收回我上面的说法:你可以使用上述策略。除非 http 是外部的,只有那时你才可以使用 httptest。我之前的建议会是更好的选择。
抱歉,我过去连续工作了12个小时。
如果你想提高 GetJSON 方法的效率,可以做一些类似 init() 方法的事情。众多例子中的一个:
type Graph struct {
ClientID string
ClientSecret string
GetJSONFx func(method string, url string) ([]byte, error)
}
func (g *Graph) Init() (err error) {
...
g.GetJSONFx = g.getServerJSON
...
}
func (g *Graph) GetJSON(method string, url string) ([]byte, error) {
return g.getServerJSON(method, url)
}
func main() {
g := &Graph{}
err := g.Init()
if err != nil {
// handle error
return nil
}
// mock it when needed
g.GetJSONFx = func(method string, url string) ([]byte, error) {
... // your custom function
}
for {
d, err := g.GetJSON(...) // calls does not need to check for GetJSONFx during work everytime
}
}
在Golang中模拟结构体方法进行测试,通常使用接口和依赖注入。以下是针对你的代码示例的解决方案:
首先,定义接口来抽象HTTP客户端:
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
type Graph struct {
ClientID string
ClientSecret string
Client HTTPClient // 使用接口类型
}
修改GetJSON方法使用注入的客户端:
func (g *Graph) GetJSON(method string, url string) ([]byte, error) {
g.RefreshToken()
request, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}
response, err := g.Client.Do(request) // 使用注入的客户端
if err != nil {
return nil, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return body, err
}
return body, nil
}
创建模拟HTTP客户端进行测试:
type MockHTTPClient struct {
DoFunc func(req *http.Request) (*http.Response, error)
}
func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
if m.DoFunc != nil {
return m.DoFunc(req)
}
return nil, nil
}
func TestGetUsers(t *testing.T) {
mockClient := &MockHTTPClient{
DoFunc: func(req *http.Request) (*http.Response, error) {
// 验证请求参数
if req.URL.String() != "https://example.com/users" {
t.Errorf("期望URL: https://example.com/users, 实际: %s", req.URL.String())
}
// 返回模拟响应
body := `{"users": [{"id": 1, "name": "test"}]}`
return &http.Response{
StatusCode: 200,
Body: ioutil.NopCloser(strings.NewReader(body)),
}, nil
},
}
graph := &Graph{
ClientID: "test-id",
ClientSecret: "test-secret",
Client: mockClient,
}
users, err := graph.GetUsers()
if err != nil {
t.Errorf("GetUsers失败: %v", err)
}
// 验证结果
if users == nil {
t.Error("期望返回用户数据")
}
}
对于RefreshToken方法的模拟,可以使用类似的方法:
type TokenRefresher interface {
RefreshToken() error
}
type MockGraph struct {
Graph
RefreshTokenFunc func() error
}
func (m *MockGraph) RefreshToken() error {
if m.RefreshTokenFunc != nil {
return m.RefreshTokenFunc()
}
return nil
}
func TestGetJSON_WithMockRefresh(t *testing.T) {
refreshCalled := false
mockGraph := &MockGraph{
RefreshTokenFunc: func() error {
refreshCalled = true
return nil
},
}
// 设置模拟HTTP客户端
mockGraph.Client = &MockHTTPClient{
DoFunc: func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: 200,
Body: ioutil.NopCloser(strings.NewReader(`{}`)),
}, nil
},
}
_, err := mockGraph.GetJSON("GET", "https://example.com/test")
if err != nil {
t.Errorf("GetJSON失败: %v", err)
}
if !refreshCalled {
t.Error("RefreshToken方法未被调用")
}
}
在生产代码中初始化Graph:
// main.go
func main() {
client := &Graph{
ClientID: "clientId",
ClientSecret: "clientsecret",
Client: &http.Client{}, // 使用真实的HTTP客户端
}
users, err := client.GetUsers()
if err != nil {
log.Fatal(err)
}
fmt.Println(users)
}
这种方法通过接口实现了依赖注入,使得在测试时可以轻松替换真实的HTTP客户端和RefreshToken方法,避免了对外部服务的真实调用。

