如何在Golang中用Testify Mock Redis客户端进行单元测试?
问题:Gin API中Mock Redis单例客户端的困境
背景
我有一个基于gin的简单Rest API,实现了Redis数据库的CRUD操作。想在单元测试中mock Redis客户端(github.com/go-redis/redis/v9),但因为有OOP背景,可能错误套用了模式。
路由代码(internal/api.go)
func GetRouter() *gin.Engine { router := gin.Default() ... router.GET("/session/:sessionid", getSession) return router }
控制器代码(internal/controlers.go)
func getSession(c *gin.Context) { client := redis_client.GetClient() sessionid := c.Param("sessionid") val, err := client.Get(c, sessionid).Result() if err == redis.Nil { fmt.Printf("key %s not exist", sessionid) c.JSON(404, fmt.Sprintf("session %s not found", sessionid)) } else if err != nil { fmt.Printf("unkown error %s", err) c.JSON(500, fmt.Sprintf(`{"error":"%s"}`, err)) } else { c.JSON(200, val) } }
Redis单例客户端(pkg/redis/redis.go)
var redisClient *redis.Client func GetClient() *redis.Client { if redisClient == nil { redisClient = redis.NewClient(&redis.Options{ Addr: os.Getenv("REDIS_HOST"), Password: os.Getenv("REDIS_PWD"), DB: 0, }) } return redisClient }
测试场景
我尝试用github.com/stretchr/testify/mock来mock redis.Client.Get或redis.StringCmd.Result(实际调用Redis的地方),但无论怎么mock,最终还是会调用localhost:6379。
Mock尝试代码
import ( .... "testing" "github.com/stretchr/testify/mock" ) type MockedRedisConn struct { mock.Mock } func (m *MockedRedisConn) Get(ctx context.Context, key string) *redis.StringCmd { ret := m.Called(ctx, key) var r0 *redis.StringCmd if ret.Get(0) != nil { r0 = ret.Get(0).(*redis.StringCmd) } return r0 } func Test_Sessions(t *testing.T) { r := api.GetRouter() redisConn := new(MockedRedisConn) var ctx = context.Background() t.Run("Should return the session", func(t *testing.T) { //mocks strCmd := redis.NewStringCmd(ctx) strCmd.SetVal(`{"session":""}`) redisConn.On("Get", mock.AnythingOfType("*gin.Context"), "00000000").Return(strCmd) //call req, _ := http.NewRequest("GET", "/session/00000000", nil) w := httptest.NewRecorder() r.ServeHTTP(w, req) responseData, _ := ioutil.ReadAll(w.Body) assert.Equal(t, `{"session":""}`, string(responseData)) assert.Equal(t, http.StatusOK, w.Code) }) }
疑问
我发现大多数mock示例中,mock对象都是被测函数的参数,但我的情况不是这样——我没办法把getSession里的客户端替换成MockedRedisConn(类似猴子补丁的方式)。想请教:
- 这种单例客户端的设置方式是否兼容mock?
- 是否应该从
*gin.Context加载客户端以便测试时替换?
内容的提问来源于stack exchange,提问作者zar3bski
相关产品推荐
相关产品推荐

