-
Notifications
You must be signed in to change notification settings - Fork 2
redis mock解决方案
Jakegogo edited this page Oct 17, 2024
·
2 revisions
其实是想mock dial
xxx 3-31 下午 4:51 应该怎么写呢
xxx 3-31 下午 4:51
xxx 3-31 下午 4:52 mock return 的时候,不知道要怎么构造 redis.Conn 的实例
mocker 3-31 下午 4:53
package redigo_test
import (
"errors"
"strconv"
"testing"
"git.code.oa.com/goom/mocker_examples/redigo"
"git.code.oa.com/goom/mocker"
"github.com/gomodule/redigo/redis"
"github.com/stretchr/testify/suite"
)
// TestUnitGetTelFromRedis 测试入口
func TestUnitGetTelFromRedis(t *testing.T) {
suite.Run(t, new(GetTelFromRedisTestSuite))
}
// PhpToolTestSuite测试套件
type GetTelFromRedisTestSuite struct {
suite.Suite
mocker *mocker.Builder
fakeErr error
}
func (s *GetTelFromRedisTestSuite) SetupTest() {
// patches初始化
s.mocker = mocker.Create()
_, err := strconv.ParseUint("-101", 10, 64)
s.fakeErr = err
}
// TestUnitGetTelFromRedis 测试函数GetTelFromRedis
func (s *GetTelFromRedisTestSuite) TestUnitGetTelFromRedis() {
s.Run("success", func() {
testCases := s.TestUnitGetTelFromRedisCase()
for _, tc := range testCases {
s.Run(tc.name, func() {
tc.prepare()
// 获取参数
param := tc.params.(string)
// 执行
result, err := redigo.GetTelFromRedis(param)
// 断言
s.Equal((tc.wantResult).([]interface{})[0], result)
s.Equal((tc.wantResult).([]interface{})[1], err)
// 重置
s.mocker.Reset()
})
}
})
}
func (s *GetTelFromRedisTestSuite) TestUnitGetTelFromRedisCase() []Case {
return []Case{
{
name: "key not exists",
prepare: func() {
conn := (redis.Conn)(nil)
s.mocker.Interface(&conn).Method("Do").
As(func(ctx *mocker.IContext, commandName string, args ...interface{}) (reply interface{}, err error) {
return nil, nil
}).Return("101", redis.ErrNil)
s.mocker.Interface(&conn).Method("Close").
Apply(func(ctx *mocker.IContext) (err error) {
return nil
})
s.mocker.Func(redis.Dial).Apply(func(network, address string, options ...redis.DialOption) (redis.Conn, error) {
return conn, nil
})
},
params: "key",
wantResult: []interface{}{
uint64(0),
errors.New("key not exists"),
},
},
}
}
// Case 测试case
type Case struct {
name string
prepare func()
params interface{}
eval func(tt Case) []interface{}
wantResult interface{}
}
这个example可以跑通
xxx 3-31 下午 4:54 conn := (redis.Conn)(nil) 一开始是这样用的,好像有问题。我再看下
mocker 3-31 下午 4:58 也可以用这种方式
package codetool
import (
"errors"
"testing"
"time"
"git.code.oa.com/goom/mocker"
"github.com/gomodule/redigo/redis"
)
func TestIsNumeric(t *testing.T) {
tests := []struct {
name string
arg interface{}
wantResult bool
}{
{
name: "int",
arg: 0,
wantResult: true,
},
{
name: "uint",
arg: uint(0),
wantResult: true,
},
{
name: "int8",
arg: int8(0),
wantResult: true,
},
{
name: "uint8",
arg: uint8(0),
wantResult: true,
},
{
name: "int16",
arg: int16(0),
wantResult: true,
},
{
name: "uint16",
arg: uint16(0),
wantResult: true,
},
{
name: "int32",
arg: int32(0),
wantResult: true,
},
{
name: "uint32",
arg: uint32(0),
wantResult: true,
},
{
name: "int64",
arg: int64(0),
wantResult: true,
},
{
name: "uint64",
arg: uint64(0),
wantResult: true,
},
{
name: "float32",
arg: float32(0),
wantResult: true,
},
{
name: "float64",
arg: float64(0),
wantResult: true,
},
{
name: "complex64",
arg: complex64(0),
wantResult: true,
},
{
name: "complex128",
arg: complex128(0),
wantResult: true,
},
{
name: "string",
arg: "3.2",
wantResult: true,
},
{
name: "invalid type",
arg: []string{},
wantResult: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotResult := IsNumeric(tt.arg); gotResult != tt.wantResult {
t.Errorf("IsNumeric() = %v, want %v", gotResult, tt.wantResult)
}
})
}
}
func TestIsStringNumeric(t *testing.T) {
tests := []struct {
name string
arg string
want bool
}{
{
name: "empty string",
arg: "",
want: false,
},
{
name: "invalid string number including -",
arg: "3-2",
want: false,
},
{
name: "invalid string number including two point",
arg: "3.2.1",
want: false,
},
{
name: "invalid string number with alphabet",
arg: "abc",
want: false,
},
{
name: "valid string number",
arg: "-3.2",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsStringNumeric(tt.arg); got != tt.want {
t.Errorf("IsStringNumeric() = %v, want %v", got, tt.want)
}
})
}
}
func TestStrtotime(t *testing.T) {
tests := []struct {
name string
timeStr string
want int64
}{
{
name: "empty time string",
timeStr: "",
want: 0,
},
{
name: "parse time error",
timeStr: "test",
want: 0,
},
{
name: "parse time success",
timeStr: "2021-03-02 14:30:00",
want: 1614666600,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Strtotime(tt.timeStr); got != tt.want {
t.Errorf("Strtotime() = %v, want %v", got, tt.want)
}
})
}
}
func TestGetTelFromRedis(t *testing.T) {
mock := mocker.Create()
mockConn := redis.NewConn(nil, time.Second, time.Second)
mockErr := errors.New("mock error")
tests := []struct {
name string
prepare func()
want uint64
wantErr bool
}{
{
name: "dial redis error",
prepare: func() {
mock.Func(redis.Dial).Return(nil, mockErr)
},
want: 0,
wantErr: true,
},
{
name: "redis get nil",
prepare: func() {
mock.Struct(mockConn).Method("Do").Return("", redis.ErrNil)
mock.Struct(mockConn).Method("Close").Return(nil)
mock.Func(redis.Dial).Return(mockConn, nil)
},
want: 0,
wantErr: true,
},
{
name: "redis get error",
prepare: func() {
mock.Struct(mockConn).Method("Do").Return("", mockErr)
mock.Struct(mockConn).Method("Close").Return(nil)
mock.Func(redis.Dial).Return(mockConn, nil)
},
want: 0,
wantErr: true,
},
{
name: "redis get invalid string number",
prepare: func() {
mock.Struct(mockConn).Method("Do").Return("abc", nil)
mock.Struct(mockConn).Method("Close").Return(nil)
mock.Func(redis.Dial).Return(mockConn, nil)
},
want: 0,
wantErr: false,
},
{
name: "parse uint error",
prepare: func() {
mock.Struct(mockConn).Method("Do").Return("3.2", nil)
mock.Struct(mockConn).Method("Close").Return(nil)
mock.Func(redis.Dial).Return(mockConn, nil)
},
want: 0,
wantErr: true,
},
{
name: "success",
prepare: func() {
mock.Struct(mockConn).Method("Do").Return("32", nil)
mock.Struct(mockConn).Method("Close").Return(nil)
mock.Func(redis.Dial).Return(mockConn, nil)
},
want: 32,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.prepare()
defer mock.Reset()
got, err := GetTelFromRedis("uid")
if (err != nil) != tt.wantErr {
t.Errorf("GetTelFromRedis() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("GetTelFromRedis() got = %v, want %v", got, tt.want)
}
})
}
}
还有一种办法: 使用miniredis 模拟一个本地的Redis, 在测试方法init 时启动一个 miniredis 生成一个本地可用的IP,然后用redis.NewClientProxy 连接就行。 这样这个Redis就可以直接读写,
import "github.com/alicebob/miniredis"
//run mini Redis
s, err := miniredis.Run()
if err != nil {
panic(err)
}
redisarr := "ip://" + s.Addr()
rediscliOK = redis.NewClientProxy("trpc.redis.server.service", client.WithTarget(redisarr))