Ensure auto-generated code is always up-to-date with compile-time assertions in Go
Tags: go, codegeneration, interface
Published: 2020-07-30 17:58:34

# Introduction
Do you get annoyed when others (or you) forget to run the code generation command and now nothing works/code is out-dated?

[Compile Time Assertions](https://commaok.xyz/post/compile-time-assertions/) to the rescue!

# Scenario
Suppose you have an interface:
```go
type MyInterface interface {
    WithMethod(method string) MyInterface
}
```
This interface has an associated implementation:
```go
type MyInterfaceImpl struct {
    privateField string
}
func (my *MyInterfaceImpl) MyInterface {
    // ... implementation code
}
```

Which you've have been auto-generating [mock objects](https://stackoverflow.com/questions/3622455/what-is-the-purpose-of-mock-objects) for using the awesome [mockery package](https://github.com/vektra/mockery). 

### Why Though
You are doing this because you want users of your library to be able to mock your library.
- Thus allowing them to isolate their own code when writing unit tests.
- Meaning we don't actually use this mock in OUR tests, it is used by OTHERS

So the interface definition above is augmented with: 
```go
// Generate mocks by running "go generate ./..."
// go:generate mockery --name MyInteface
type MyInterface interface {
    WithMethod(method string) MyInterface
}
```
Which produces when `go generate ./...` is ran:
`mocks/MyInterface.go`
```go
// MyInterface is an autogenerated mock type for the MyInterface type
type MyInterface struct {
	mock.Mock
}

// WithMethod provides a mock function with given fields: method
func (_m *MyInterface) WithMethod(method string) (pkg.MyInterface) {
// ... some mock implementation code
```

# Problem
Now what happens when Greg who isn't aware of the auto-generated code, adds another method to `MyInterface` and `MyInterfaceImpl`:
```go
type MyInterface interface {
    WithMethod(method string) MyInterface
    WithAnotherMethod(method string) MyInterface // New
}
```

Since the `MyInterface` has changed but Greg didn't auto-generate the mocks using `go generate ./...`, the mock object will not fulfill the interface and cannot be used for testing by downstream users (Thanks Greg)!

# Solution
Add a [compile-assertion](https://commaok.xyz/post/compile-time-assertions/) above the interface definition after you have initially generated the mock object (Also add --inpackage flag to go generate command to avoid import cycles).
```go
// Generate mocks by running "go generate ./..."
// go:generate mockery --name MyInteface --inpackage
var _ MyInterface = &MockMyInterface{} // COMPILE-TIME Assertion
type MyInterface interface {
    WithMethod(method string) MyInterface
}
```

Now if Greg adds another method but forgets to run `go generate`
```go
var _ MyInterface = &MockMyInterface{}
type MyInterface interface {
    WithMethod(method string) MyInterface
    WithAnotherMethod(method string) MyInterface // New
}
```
He will get hit with:
```
cannot use &MockMyInterface literal (type *MockMyInterface) as type MyInterface in assignment:
	*MockMyInterface does not implement MyInterface (missing WithAnotherMethod method)
```

At which point, he might realize that he needs to add the method to the mock object using `go generate`.

Caveat: You will need to comment out the compile-time assertion `var _ MyInterface = &MockMyInterface{}` when running `go generate`/`mockery` tool