counterfeiter is a command-line tool for generating fake implementation of Go interfaces.
One of the best things we can do when writing Go programs is to design our components’ dependencies to be based on local interfaces. By local I mean interfaces which are needed and defined by a given component. Consider the following example: we have a http server, a service component and a database dependency.
1
2
3
4
5
6
7
8
9
10
11
12
|
package main
func main() {
// initialize some kind of database
db := storage.New()
// our service depends on a database
svc := service.New(db)
http.HandleFunc("/example", svc.Example)
log.Fatal(http.ListenAndServe(":8080", nil))
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package storage
type DB struct {}
type Data struct {}
func New() *DB {
return &DB{}
}
func (db *DB) SomeData() (*Data, error) {
... // return data from database
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package service
type Storage interface {
SomeData() (*storage.Data, error)
}
type Data struct{
Value int
}
type Service struct {
db Storage
}
func New(db Storage) *Service {
return &Service{db:db}
}
func (s *Service) Example(w http.ResponseWriter, r *http.Request) {
// do something that requires using the database
data, err := s.db.SomeData()
if err != nil {
...
}
// do something with data and write response
...
}
|
The service component has a database dependency. It may be any kind of external object - client to another service, a cache/database, message queue, whatever. This dependency is expressed as an interface inside the service package. In effect, the service is telling its initialiser: when you create me, please give me an object which implements my service.Storage
interface, cause that’s what I need to execute my functions.
This design has some nice benefits. The service component doesn’t know and care how the storage dependency is implemented. All it knows and cares about is that it has a SomeData() (*storage.Data, error)
function. We can supply whatever object we wish to the service, as long as it implements the interface.
Here comes the counterfeiter