redigoのPoolとredigomockを利用する方法
redigoのredis.Poolはredis.Connとは異なりインタフェースではなく構造体として定義されています.
それとredigomockはredis.Poolを生成する機能を有していないことからぱっと見使うことができないだろうなっと判断しましたが
よくよく考えてみるとredis.Poolで利用する関数群をインタフェースとして定義すれば良いのではないかと気づきがあったので共有します.
以下Example
// service.go
package service
import (...) // 省略
type RedigoPool interface {
Get() redis.Conn
Close() error
}
type ExampleService struct {
pool RedigoPool // *redis.Poolを直接定義せず, RedigoPoolというインタフェースを利用する
}
func (s *ExampleService) HmSetWithExpire(key string, a, b int64, expireSeconds uint) (interface{}, error) {
redisConn := s.pool.Get()
defer redisConn.Close()
redisConn.Send("MULTI")
redisConn.Send("HMSET", key, "a", a, "b", b)
redisConn.Send("EXPIRE", key, expireSeconds)
reply, err := redisConn.Do("EXEC")
if err != nil {
return nil, err
}
return reply, nil
}
// service_test.go
package service
import (...) // 省略
// redigoMockPool redigomock用RedisPool
type redigoMockPool struct {
conn redis.Conn
}
func (m redigoMockPool) Get() redis.Conn {
return m.conn
}
func (m redigoMockPool) Close() error {
return m.conn.Close()
}
func TestExampleService_HmSetWithExpire(t *testing.T) {
mockConn := redigomock.NewConn()
mockPool := &redigoMockPool{
conn: mockConn,
}
svc := ExampleService{
pool: mockPool,
}
key := "test-uhyouhyo!"
a := int64(1)
b := int64(2)
expireSeconds := uint(3200)
cmd1 := mockConn.Command("MULTI")
cmd2 := mockConn.Command("HMSET", key, "a", a, "b", b)
cmd3 := mockConn.Command("EXPIRE", key, expireSeconds)
cmd4 := mockConn.Command("EXEC").Expect([]interface{}{"OK", "OK"})
_, err := svc.HmSetWithExpire(key, a, b, expireSeconds)
if err != nil {
t.Error(err)
}
if counter := mockConn.Stats(cmd1); counter != 1 {
t.Errorf("Expected cmd1 to be called once but was called %d times", counter)
}
if counter := mockConn.Stats(cmd2); counter != 1 {
t.Errorf("Expected cmd2 to be called once but was called %d times", counter)
}
if counter := mockConn.Stats(cmd3); counter != 1 {
t.Errorf("Expected cmd3 to be called once but was called %d times", counter)
}
if counter := mockConn.Stats(cmd4); counter != 1 {
t.Errorf("Expected cmd4 to be called once but was called %d times", counter)
}
}
上記のコードのRedigoPool
が味噌で予めredis.Poolで利用する関数をインタフェースに定義しておけば, 割とさくっとMockを利用可能になります.
ちなみにredis.Pool#Dial
でredigomock.Conn
を取得できるようにすればいいのでは? っとおもう方もいると思いますが,
redis.Conn
を取得するredis.Pool#Get
は複雑なことをやってるのでうまいことredigomock.Conn
を取得することができません.