Go语言单元测试中如何Mock Memcache?无接口场景的解决方法
Got it, let's work through this problem step by step. The root issue here is that your current code is tied directly to the concrete memcache implementation instead of an abstract interface—this is why gomock can't help you mock it out for testing. Here's a straightforward, actionable approach to fix this:
First, we need to abstract the cache operations you need into an interface. This lets us swap out the real memcache client with a mock later. Adjust the return types to match your actual getAccessTokenFromCache function (I'm assuming it returns a token string and error here):
// CacheInterface defines the cache methods our code depends on type CacheInterface interface { GetAccessToken(accessToken string) (string, error) }
Next, create a struct that wraps your actual memcache client and implements this interface. This keeps your production code using the real memcache while giving us a way to mock it in tests:
// MemcacheClient wraps the real memcache client and implements CacheInterface type MemcacheClient struct { client *memcache.Client // Use your actual memcache client type here } // GetAccessToken implements the CacheInterface method func (m *MemcacheClient) GetAccessToken(accessToken string) (string, error) { // Copy the logic from your original getAccessTokenFromCache function here item, err := m.client.Get(accessToken) if err != nil { return "", err } return string(item.Value), nil }
Update your business logic (like the SendData function) to depend on the CacheInterface instead of calling getAccessTokenFromCache directly. This is the key to making your code testable:
// Example: Refactored SendData that depends on CacheInterface func SendData(cache CacheInterface, accessToken string, req *http.Request) error { // Use the interface instead of direct function call validToken, err := cache.GetAccessToken(accessToken) if err != nil { return err } // Rest of your authorization check and business logic goes here // ... return nil }
Now that we have an interface, we can generate a mock with gomock and use it in your test:
- First, install gomock and mockgen if you haven't:
go install github.com/golang/mock/mockgen@latest
- Generate the mock file. Replace
your/package/pathwith the path to where you definedCacheInterface:
mockgen -source=your/package/path/cache.go -destination=mocks/mock_cache.go -package=mocks
- Update your test to use the mock:
func TestSendData(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() // Create the mock cache instance mockCache := mocks.NewMockCacheInterface(mockCtrl) // Set up expectation: when GetAccessToken is called with "abcd", return a valid token (or error if needed) mockCache.EXPECT().GetAccessToken("abcd").Return("valid_test_token", nil).Times(1) // Prepare your test request jsonStr := []byte(`{ "devices": [ {"id": "avccc", "data":"abcd/" } ] }`) req, err := http.NewRequest("POST", "/your-endpoint", bytes.NewBuffer(jsonStr)) if err != nil { t.Fatalf("Failed to create test request: %v", err) } // Call your business function with the mock cache err = SendData(mockCache, "abcd", req) if err != nil { t.Errorf("SendData failed unexpectedly: %v", err) } }
This setup lets you completely bypass the real memcache—you can define exactly what GetAccessToken returns, so you can skip the actual authorization check in tests.
If you don't want to use gomock, you can create a lightweight fake implementation for testing:
// FakeCache is a simple in-memory cache for tests type FakeCache struct { tokens map[string]string } func NewFakeCache() *FakeCache { return &FakeCache{tokens: make(map[string]string)} } func (f *FakeCache) GetAccessToken(accessToken string) (string, error) { token, exists := f.tokens[accessToken] if !exists { return "", fmt.Errorf("token not found") } return token, nil }
Then use it in your test:
func TestSendDataWithFakeCache(t *testing.T) { fakeCache := NewFakeCache() fakeCache.tokens["abcd"] = "valid_test_token" jsonStr := []byte(`{ "devices": [ {"id": "avccc", "data":"abcd/" } ] }`) req, err := http.NewRequest("POST", "/your-endpoint", bytes.NewBuffer(jsonStr)) if err != nil { t.Fatalf("Failed to create test request: %v", err) } err = SendData(fakeCache, "abcd", req) if err != nil { t.Errorf("SendData failed unexpectedly: %v", err) } }
The core idea here is always the same: depend on abstractions (interfaces) instead of concrete implementations. That's what makes mocking and testing possible in Go.
内容的提问来源于stack exchange,提问作者Swagata Mondal

