https://github.com/go-spring/gs-mock
A modern and type-safe mocking library for Go with full support for generics.
https://github.com/go-spring/gs-mock
Last synced: 26 days ago
JSON representation
A modern and type-safe mocking library for Go with full support for generics.
- Host: GitHub
- URL: https://github.com/go-spring/gs-mock
- Owner: go-spring
- License: apache-2.0
- Created: 2025-06-14T09:15:24.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-08-02T02:02:16.000Z (11 months ago)
- Last Synced: 2025-08-09T21:51:51.828Z (10 months ago)
- Language: Go
- Size: 141 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# gs-mock
[English](README.md) | [中文](README_CN.md)
> The project has been officially released, welcome to use!
`gs-mock` is a modern, type-safe Go mock library with **full support for generics**.
It addresses the shortcomings of traditional Go mock tools in terms of **type safety** and **usability**,
and natively supports concurrent testing through `context.Context` propagation.
`gs-mock` supports mocking the following targets:
* Interfaces (via code generation)
* Plain functions
* Struct methods
It is especially suitable for **unit testing** and **component testing** in microservice architectures.
## Features
### Type System and Language Features
* **Type Safety & Generics Support**
* Native support for generic interfaces and generic functions
* Full type inference and auto-completion provided by IDEs
* **Multiple Parameters and Multiple Return Values**
* Supports up to **7 parameters**
* Supports up to **4 return values**
* Covers the vast majority of real-world business function signatures
### Mocking Modes
* **Handle Mode**
* Encapsulates the entire mock logic in a single callback
* Suitable for scenarios with complex conditional logic
* **When / Return Mode**
* Returns results based on condition matching
* Better suited for declarative and highly readable mock configurations
### Mock Target Types
* **Interface Mocking**
* Automatically generates mock implementations via code generation
* No reliance on `context.Context` parameter
* **Plain Function & Struct Method Mocking**
* Propagates mock configuration through `context.Context`
* No need to introduce additional interfaces for functions or methods
### Concurrency and Context
* **Concurrent Testing Support**
* Passes the Mock Manager via `context.Context`
* Ensures isolation and safety of mocks in concurrent test scenarios
## Installation
### Standalone Installation
```
go install github.com/go-spring/gs-mock@latest
```
### Installation via the gs Toolchain
```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/go-spring/gs/HEAD/install.sh)"
```
## Quick Start
### 1. Interface Mocking
#### 1. Define an Interface
```
type Service interface {
Do(n int, s string) (int, error)
Format(s string, args ...any) string
}
```
#### 2. Generate Mock Code
```
//go:generate gs mock -o src_mock.go
```
> `gs mock` indicates using the `mock` subcommand from the `gs` toolchain.
> You may also directly use the `gs-mock` command.
After adding the directive at the top of the interface file, mock code will be generated for **all interfaces in the
current package**. If you only want to generate mocks for specific interfaces, use the `-i` option:
```
//go:generate gs-mock -o src_mock.go -i '!RepositoryV2,Repository'
```
**Examples of the `-i` option:**
* `-i 'Repository'`
Generate a mock only for the `Repository` interface
* `-i '!Repository'`
Generate mocks for all interfaces except `Repository`
* `-i 'Repository,Service'`
Generate mocks only for `Repository` and `Service`
* `-i '!Repository,Service'`
Generate mocks for all interfaces except `Repository`, but include `Service`
#### 3. Using Mocks (Handle Mode)
```
r := gsmock.NewManager()
s := NewServiceMockImpl(r)
// Handle mode: determine return logic based on input arguments
s.MockDo().Handle(func (n int, s string) (int, error) {
if n%2 == 0 {
return n * 2, nil
}
return n + 1, errors.New("error")
})
fmt.Println(s.Do(1, "abc")) // 2 error
fmt.Println(s.Do(2, "abc")) // 4
```
#### 4. Using Mocks (When / Return Mode)
```
r := gsmock.NewManager()
s := NewServiceMockImpl(r)
// For args[0] == "abc"; variadic arguments are represented as a slice
s.MockFormat().When(func (s string, args []any) bool {
return args[0] == "abc"
}).ReturnValue("abc")
// For args[0] == "123"; variadic arguments are represented as a slice
s.MockFormat().When(func (s string, args []any) bool {
return args[0] == "123"
}).ReturnValue("123")
fmt.Println(s.Format("", "abc", "123")) // abc
fmt.Println(s.Format("", "123", "abc")) // 123
fmt.Println(s.Format("", "xyz", "abc")) // panic: no matching mock found
```
> **Notes**
>
> * Do not mix `Handle` mode and `When/Return` mode on the same method
> * When multiple `When/Return` configurations exist, they are matched in registration order; the first successful match
is executed
### 2. Function Mocking
#### 1. Define a Plain Function
```
//go:noinline // Prevent the function from being inlined
func Do(ctx context.Context, n int) int { return n }
```
#### 2. Mock the Function
```
r := gsmock.NewManager()
ctx := gsmock.WithManager(context.TODO(), r)
// Return a fixed value using ReturnValue
gsmock.Func21(Do, r).ReturnValue(2)
fmt.Println(Do(ctx, 1)) // 2
```
**Explanation:**
* Requirements for mocking plain functions:
* The first parameter must be `context.Context`
* Mock configuration is propagated via the context chain
* `Func21` means:
* 2 parameters
* 1 return value
* For variadic functions, use the `VarFuncNN` series, such as `VarFunc21`
### 3. Struct Method Mocking
#### 1. Define a Struct Method
```
type Service struct{ m int }
func (s *Service) Do(ctx context.Context, n int) int {
return n
}
```
#### 2. Mock the Struct Method
```
r := gsmock.NewManager()
ctx := gsmock.WithManager(context.TODO(), r)
// The first parameter becomes the receiver type, followed by the original method parameters
gsmock.Func31((*Service).Do, r).Handle(func(s *Service, ctx context.Context, n int) int {
return n + s.m
})
fmt.Println((&Service{m: 1}).Do(ctx, 1)) // 2
fmt.Println((&Service{m: 2}).Do(ctx, 1)) // 3
```
**Notes:**
* Use a **method expression** (e.g. `(*Service).Do`) instead of a method value (e.g. `s.Do`)
* The receiver becomes the **first parameter** of the mock callback; `ctx` becomes the **second parameter**
* Tests must be run with `-gcflags="all=-N -l"` to prevent method inlining
> More examples and usage can be found in the [example](example) directory.
## FAQ
### 1. Mock Failure Due to Inlining Optimization
* **Problem**:
In some cases, functions or methods are inlined by the Go compiler, causing mock logic not to be triggered.
* **Solution**:
Explicitly disable compiler optimizations when running tests:
```
go test -gcflags="all=-N -l" ./...
```
* **Explanation**:
This disables inlining and certain optimizations, ensuring the mock framework can correctly intercept calls.
### 2. Context Parameter Requirement
* **Problem**:
When mocking plain functions or struct methods, the first parameter must be `context.Context`.
* **Solution**:
When designing testable functions, include `context.Context` as the first parameter to allow mock manager propagation.
* **Notes**:
* This restriction **only applies to plain functions and struct method mocking**
* **Interface mocks do not require** `context.Context` in method signatures
### 3. When / Return Registration Order
* **Problem**:
When multiple `When/Return` rules are registered for the same method, matching order affects the outcome.
* **Solution**:
**The first successfully matched rule is executed**, so register rules from **most specific to most general**.
### 4. Manager Scope and Concurrency Safety
* **Problem**:
The `Mock Manager` itself is not goroutine-safe. Registering mocks dynamically during concurrent execution may cause
unpredictable behavior.
* **Solution**:
All mock registrations must be completed **before any concurrent logic starts**, and the manager should be passed to
goroutines via `context.Context`.
### 5. Mocking Variadic Functions
* **Problem**:
Variadic functions (e.g. `Printf(format string, args ...any)`) have different parameter structures during mocking and
require special handling.
* **Solution**:
Use the `VarFuncNN` series to mock variadic functions.
* **Explanation**:
* Variadic arguments are wrapped as a single slice parameter in the mock callback
* All variadic mock types are prefixed with `Var`
## License
This project is licensed under the Apache License Version 2.0.