Unit Testing with go-redis
Introduction: Using MiniRedis for Unit Testing
After joining Tencent, unit testing has become an indispensable part of my development process. Recently, I encountered a scenario where I needed to add caching, and I chose to use go-redis. After completing the business functionality, the next challenge was how to properly test it.
On the recommendation of my team, I discovered that MiniRedis is a really nice project for this purpose. I decided to use MiniRedis for testing (emphasizing: you should use **dependency injection for your design!**).
Here is the simplified structure of my code (irrelevant logic has been omitted, keeping only the minimal implementation):
// Settings database model
type Settings struct {
UID uint64 `gorm:"column:uid" json:"uid"`
}
// UnmarshalBinary: Deserialize for extracting cache
func (s *Settings) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, s)
}
// MarshalBinary: Serialize for caching
func (s *Settings) MarshalBinary() ([]byte, error) {
return json.Marshal(s)
}
// SettingsRedis: Wrapper for the Redis client
type SettingsRedis struct {
rdb redis.UniversalClient
}
// keySettings: Generate the Redis key for settings
func keySettings(uid uint64) string {
return fmt.Sprintf("SETTINGS:%d", uid)
}
// NewSettingsRedis: Constructor using dependency injection
func NewSettingsRedis(rdb redis.UniversalClient) *SettingsRedis {
return &SettingsRedis{
rdb: rdb,
}
}
// Get: Retrieve settings from Redis
func (s *SettingsRedis) Get(ctx context.Context, uid uint64) *Settings {
var settings Settings
if err := s.rdb.Get(ctx, keySettings(uid)).Scan(&settings); err != nil {
// Handle error if necessary
}
return &settings
}
// Delete: Remove settings from Redis
func (s *SettingsRedis) Delete(ctx context.Context, uid uint64) {
if err := s.rdb.Del(ctx, keySettings(uid)).Err(); err != nil {
// Handle error if necessary
}
}
// Set: Store settings into Redis
func (s *SettingsRedis) Set(ctx context.Context, settings *Settings) {
err := s.rdb.Set(ctx, keySettings(settings.UID), settings, cacheTimeout).Err()
if err != nil {
// Handle error if necessary
}
}
We need to verify whether the Set, Delete, and Get functionalities behave as expected. Unit testing is indispensable for this.
Installation
Install MiniRedis with:
go get github.com/alicebob/miniredis/v2
Then import it:
import "github.com/alicebob/miniredis/v2"
Create a new mock Redis client:
func newRDB(t *testing.T) *redis.Client {
s := miniredis.RunT(t)
rdb := redis.NewClient(&redis.Options{
Addr: s.Addr(),
})
return rdb
}
By using newRDB, we mock the original redis.UniversalClient and use dependency injection like this:
settingsCache := NewSettingsRedis(rdb)
This setup allows us to complete the verification of the caching functionality.
Unit Testing Example
func TestSettingsRedis_Get(t *testing.T) {
rdb := newRDB(t)
settingsCache := NewSettingsRedis(rdb) // mock the original redis client
type testcase struct {
uid uint64
settings *Settings
want *Settings
}
t.Run("normal test", func(t *testing.T) {
tc := testcase{
uid: settingsData.UID,
settings: settingsData,
want: settingsData,
}
settingsCache.Set(ctx, tc.settings)
settings := settingsCache.Get(ctx, tc.uid)
assert.EqualValues(t, settings, tc.want)
})
t.Run("not exist test", func(t *testing.T) {
tc := testcase{
uid: 121,
}
settings := settingsCache.Get(ctx, tc.uid)
assert.Nil(t, settings)
})
}
Conclusion
This was my first time using go-redis together with MiniRedis for unit testing. There may be some oversights, and I welcome any feedback or corrections.
I hope this article is helpful to you!
Top comments (0)