openimsdk/openkf

Enhancing Unit Testing Through Interface-Based Redis/Mysql Client Design

Closed this issue · 1 comments

cubxxw commented

Description:

As our project matures, ensuring the robustness of our unit tests becomes critical. Our recent discussions suggest an approach that would not only improve the granularity of our testing but also future-proof our application against changes in third-party libraries or their versions.

Main Points:

  1. Configuration Management:
    • Propose passing configuration during initialization. It will make changes to the configuration smoother without the need for additional transfers later on.
    • Direct access to the config in multiple places complicates validation and can introduce potential points of failure.
  2. Interface-Based Redis Client:
    • Introduce the RedisClient interface to abstract the interaction with Redis.
    • RealRedisClient: Implementation for actual Redis interactions.
    • MockRedisClient: Memory-based mock implementation for unit testing purposes.
  3. Advantages:
    • Enhances unit test granularity: Current functions are not very supportive of unit tests and rely more on integration tests. The mock design can help address this.
    • Decoupling from the actual Redis implementation: Any upgrades or changes to the Redis library will not impact business logic. The implementation details are abstracted away behind the RedisClient interface.
    • Memory-based mock (MockRedisClient) is suitable for quick and isolated unit tests, reducing dependencies on external systems during testing.

Examples:

package main
import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)
// RedisClient 接口定义了与 Redis 客户端交互的方法
type RedisClient interface {
    Get(ctx context.Context, key string) *redis.StringCmd
    Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd
}
// RealRedisClient 实现了 RedisClient 接口,并使用真实的 Redis 客户端
type RealRedisClient struct {
    client *redis.Client
}
func NewRealRedisClient() RedisClient {
    client := redis.NewClient(&redis.Options{
        Addr: "localhost:6379", // Redis 服务器地址
    })
    return &RealRedisClient{client}
}
func (r *RealRedisClient) Get(ctx context.Context, key string) *redis.StringCmd {
    return r.client.Get(ctx, key)
}
func (r *RealRedisClient) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd {
    return r.client.Set(ctx, key, value, expiration)
}
// MockRedisClient 是一个模拟的 Redis 客户端,用于测试
type MockRedisClient struct {
    data map[string]string
}
func NewMockRedisClient() RedisClient {
    return &MockRedisClient{
        data: make(map[string]string),
    }
}
func (m *MockRedisClient) Get(ctx context.Context, key string) *redis.StringCmd {
    value, ok := m.data[key]
    if !ok {
        return redis.NewStringResult("", redis.Nil)
    }
    return redis.NewStringResult(value, nil)
}
func (m *MockRedisClient) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd {
    m.data[key] = fmt.Sprintf("%v", value)
    return redis.NewStatusResult("OK", nil)
}

Next Steps:

  • Refactor functions and methods to support the new RedisClient design.

  • Update unit tests to utilize MockRedisClient.

  • Review and validate configuration management suggestions.

kubbot commented

This issue is available for anyone to work on. Make sure to reference this issue in your pull request. ✨ Thank you for your contribution! ✨
Join slack 🤖 to connect and communicate with our developers.
If you wish to accept this assignment, please leave a comment in the comments section: /accept.🎯