https://github.com/hoenn/mimic
Mimic generates lightweight fake implementations of interfaces to inject as dependencies in tests
https://github.com/hoenn/mimic
fakes go golang mocks testing
Last synced: 3 months ago
JSON representation
Mimic generates lightweight fake implementations of interfaces to inject as dependencies in tests
- Host: GitHub
- URL: https://github.com/hoenn/mimic
- Owner: hoenn
- License: mit
- Created: 2024-01-24T20:50:20.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2026-03-26T15:00:25.000Z (3 months ago)
- Last Synced: 2026-03-27T05:32:20.824Z (3 months ago)
- Topics: fakes, go, golang, mocks, testing
- Language: Go
- Homepage:
- Size: 81.1 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# `mimic`
`mimic` is a tool for generating stubbed implementations, or fakes, of interfaces to inject as dependencies during tests that are as light as possible. Other tools like `mockgen` and `counterfeiter` bring in a ton of functionality that is excessive for some projects and make much larger generated fakes. `mimic` generates the bare minimum.
With this simplistic implementation you can recreate missing features like call counts, thread safety, returns for call, etc., in each stub as needed.
### Usage
Use with a `go:generate` directive:
```go
package foo
//go:generate go run github.com/hoenn/mimic InterfaceA,InterfaceB
type InterfaceA interface {}
// ...
type InterfaceB interface {}
```
You can also specify the file the interface appears in in the generate directive, which can be useful if you want a single `generate.go` file.
```go
package foo
// Generates fileA_fake.go and fileB_fake.go
//go:generate go run github.com/hoenn/mimic ./fileA.go InterfaceA,InterfaceB
//go:generate go run github.com/hoenn/mimic ./fileB.go InterfaceC
```
## Functionality Matrix
| Feature | Status | Notes |
|---|---|---|
| Basic methods | Yes | Named params, results, zero values |
| Unnamed params | Yes | Auto-named `arg0`, `arg1`, ... |
| Variadic params | Yes | Forwarded with `...` |
| Empty interfaces | Yes | Generates empty struct |
| All builtin types | Yes | Correct zero values |
| Pointer/slice/map/chan/func returns | Yes | Returns `nil` |
| Array returns | Yes | Returns `[N]T{}` |
| `interface{}`/`any` returns | Yes | Returns `nil` |
| Named struct returns | Yes | Returns `T{}` |
| `pkg.Type` returns | Yes | Returns `pkg.T{}` |
| Multiple interfaces per file | Yes | Comma-separated names |
| Same-file embedded interfaces | Yes | Recursive resolution |
| Transitive embeds (A→B→C) | Yes | Recursive |
| Mixed embeds + methods | Yes | Both collected |
| Cross-package interface and parameter embeds (`io.Reader`) | Yes | Handled |
| Compile time interface assertions | Yes* | Leaves a comment instead for unsupported complex type constraints |
| Generic interfaces (`T[K]`) | Yes | Type params + constraints preserved |
| Diamond embedding dedup | No | Will generate duplicate fields |
| Overlapping method dedup (Go 1.14+) | No | Same issue |
| Type constraint unions (`~int \| string`) | No | Not handled |
### Same-package generation
`mimic` generates fakes into the same package as the interface definition. This is a deliberate design choice as same-package generation has full visibility into unexported types and avoids circular dependencies. Interfaces with unexported types in their method signatures work without any additional configuration. For example:
``` go
type message string
type Client interface {
Send(msg *message) error // unexported type
}
```
Other tools that generate fakes into separate packages cannot compile fakes for an interface like this, as the unexported type will be unavailable in the `fakefoo` package.
The tradeoff for this functionality is that generated fakes are compiled with production code. In practice this should have minimal impact since the generated fake is a lightweight struct.
### Examples
```go
// testdata.go where an interface is defined.
package testdata
type Job struct {
name string
}
type JobQueuer interface {
Enqueue(j *Job) error
Dequeue(j *Job) error
Queue() []*Job
}
// testdata_fake.go where the fake is generated.
// Code generated by mimic; DO NOT EDIT.
package testdata
// FakeJobQueuer implements JobQueuer.
type FakeJobQueuer struct {
EnqueueStub func(j *Job) error
DequeueStub func(j *Job) error
QueueStub func() []*Job
}
var _ JobQueuer = (*FakeJobQueuer)(nil)
func (fakeImpl *FakeJobQueuer) Enqueue(j *Job) error {
if fakeImpl.EnqueueStub != nil {
return fakeImpl.EnqueueStub(j)
}
return nil
}
func (fakeImpl *FakeJobQueuer) Dequeue(j *Job) error {
if fakeImpl.DequeueStub != nil {
return fakeImpl.DequeueStub(j)
}
return nil
}
func (fakeImpl *FakeJobQueuer) Queue() []*Job {
if fakeImpl.QueueStub != nil {
return fakeImpl.QueueStub()
}
return nil
}
// testdata_test.go where we inject our fake for testing and
// define the functions to assert behaviors of the dependent.
func TestJobQueuer(t *testing.T) {
f := &FakeJobQueuer{
EnqueueStub: func(j *Job) error {
// Implement behavior you want to fake, like returning
// a specific error.
},
DequeueStub: func(j *Job) error {
// Or use a bool to check if a function is ever called.
// dequeueCalled = true
// dequeueCalls++
},
// Undefined QueueStub() returns zero value when called.
}
svc := newComplicatedService(f)
// Expected dequeueCalled to be true
// Expected some error to happen when queueing
}
```