在 golang 框架中编写依赖于外部服务的单元测试可以使用以下技巧:模拟外部服务行为:定义并模拟服务接口。使用模拟类型来创建测试并断言外部服务行为。实战案例:使用模拟类型测试依赖于 http 服务的外部服务。模拟服务端行为,并更新客户端指向模拟服务端。执行测试并断言结果。
如何在 GoLang 框架中编写依赖外部服务的单元测试
简介
编写依赖于外部服务的单元测试可能是一项艰巨的任务。在本指南中,我们将介绍在 GoLang 框架中编写此类测试的技巧。
立即学习“go语言免费学习笔记(深入)”;
模拟外部服务
一种常见的技术是模拟外部服务行为。我们使用 [mock](https://github.com/golang/mock) 包来定义接口和模拟类型。
package service
// MyService 接口定义了外部服务行为
type MyService interface {
GetData() (string, error)
}
// MyServiceMock 是 MyService 接口的模拟类型
type MyServiceMock struct {
mock.Mock
}
// GetData 模拟外部服务的 GetData 方法
func (m *MyServiceMock) GetData() (string, error) {
ret := m.Called()
return ret.Get(0).(string), ret.Error(1)
}
使用模拟类型
我们可以使用模拟类型来创建单元测试并断言外部服务的行为。
package service
import (
"testing"
"time"
)
func TestService_GetData(t *testing.T) {
// 创建模拟服务
mockService := &MyServiceMock{}
// 设置 mock 行为
mockService.On("GetData").Return("sample data", nil).Once()
service := NewService(mockService, time.Duration(0))
data, err := service.GetData()
// 断言
if err != nil {
t.Fatal(err)
}
if data != "sample data" {
t.Fatalf("Expected 'sample data', got '%s'", data)
}
// 验证调用是否发生
if !mockService.AssertCalled(t, "GetData") {
t.Error("GetData() was not called")
}
}
实战案例
以下是如何在集成测试中使用模拟类型测试依赖于外部服务的 HTTP 服务的示例:
package service_test
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestService_GetData(t *testing.T) {
type args struct {
ctx context.Context
r *http.Request
}
tests := []struct {
name string
args args
mockStatusCode int
expectedBody string
}{
{
"Success",
args{context.Background(), new(http.Request)},
http.StatusOK,
"sample data",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 模拟服务端行为
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Errorf("Unexpected HTTP method: %s", r.Method)
}
w.WriteHeader(tt.mockStatusCode)
_, _ = io.WriteString(w, tt.expectedBody)
}))
defer server.Close()
// 更新请求以指向模拟服务端
baseClient := Service.GetClient()
t.Cleanup(func() { Service.SetClient(baseClient) })
Service.SetClient(http.Client{Transport: &http.Transport{
Base: &http.Transport{DisableCompression: true},
DialTLS: func(network, addr string) (net.Conn, error) {
return net.Dial(network, strings.Replace(addr, "localhost", server.Listener.Addr().String(), 1))
},
}})
// 执行测试
result, err := Service.GetData(tt.args.ctx, tt.args.r)
assert.Nil(t, err)
assert.Equal(t, tt.expectedBody, result)
})
}
}