Add static interface implementation check

Add an additional line of code per interface to the generated mock file,
which allows the go compiler to statically check if the mock implements
the mocked interface.
This commit is contained in:
Lucas Bremgartner 2018-06-04 14:22:28 +02:00 committed by Lucas Bremgartner
parent c4521bcc9d
commit b21592468b
9 changed files with 93 additions and 4 deletions

View File

@ -13,6 +13,10 @@ var (
lockPersonStoreMockGet sync.RWMutex lockPersonStoreMockGet sync.RWMutex
) )
// Ensure, that PersonStoreMock does implement PersonStore.
// If this is not the case, regenerate this file with moq.
var _ PersonStore = &PersonStoreMock{}
// PersonStoreMock is a mock implementation of PersonStore. // PersonStoreMock is a mock implementation of PersonStore.
// //
// func TestSomethingThatUsesPersonStore(t *testing.T) { // func TestSomethingThatUsesPersonStore(t *testing.T) {

View File

@ -13,6 +13,10 @@ var (
lockMyInterfaceMockTwo sync.RWMutex lockMyInterfaceMockTwo sync.RWMutex
) )
// Ensure, that MyInterfaceMock does implement MyInterface.
// If this is not the case, regenerate this file with moq.
var _ MyInterface = &MyInterfaceMock{}
// MyInterfaceMock is a mock implementation of MyInterface. // MyInterfaceMock is a mock implementation of MyInterface.
// //
// func TestSomethingThatUsesMyInterface(t *testing.T) { // func TestSomethingThatUsesMyInterface(t *testing.T) {

View File

@ -2,12 +2,12 @@ package main
import ( import (
"bytes" "bytes"
"errors"
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"errors"
"github.com/matryer/moq/pkg/moq" "github.com/matryer/moq/pkg/moq"
) )

View File

@ -171,6 +171,10 @@ func (m *Mocker) Mock(w io.Writer, name ...string) error {
doc.Imports = append(doc.Imports, stripVendorPath(pkgToImport)) doc.Imports = append(doc.Imports, stripVendorPath(pkgToImport))
} }
if tpkg.Name() != m.pkgName {
doc.SourcePackagePrefix = tpkg.Name() + "."
}
var buf bytes.Buffer var buf bytes.Buffer
err = m.tmpl.Execute(&buf, doc) err = m.tmpl.Execute(&buf, doc)
if err != nil { if err != nil {
@ -250,6 +254,7 @@ func pkgInfoFromPath(src string) (*loader.PackageInfo, error) {
type doc struct { type doc struct {
PackageName string PackageName string
SourcePackagePrefix string
Objects []obj Objects []obj
Imports []string Imports []string
} }

View File

@ -42,6 +42,40 @@ func TestMoq(t *testing.T) {
} }
} }
func TestMoqWithStaticCheck(t *testing.T) {
m, err := New("testpackages/example", "")
if err != nil {
t.Fatalf("moq.New: %s", err)
}
var buf bytes.Buffer
err = m.Mock(&buf, "PersonStore")
if err != nil {
t.Errorf("m.Mock: %s", err)
}
s := buf.String()
// assertions of things that should be mentioned
var strs = []string{
"package example",
"var _ PersonStore = &PersonStoreMock{}",
"type PersonStoreMock struct",
"CreateFunc func(ctx context.Context, person *Person, confirm bool) error",
"GetFunc func(ctx context.Context, id string) (*Person, error)",
"func (mock *PersonStoreMock) Create(ctx context.Context, person *Person, confirm bool) error",
"func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*Person, error)",
"panic(\"PersonStoreMock.CreateFunc: method is nil but PersonStore.Create was just called\")",
"panic(\"PersonStoreMock.GetFunc: method is nil but PersonStore.Get was just called\")",
"lockPersonStoreMockGet.Lock()",
"mock.calls.Get = append(mock.calls.Get, callInfo)",
"lockPersonStoreMockGet.Unlock()",
"// ID is the id argument value",
}
for _, str := range strs {
if !strings.Contains(s, str) {
t.Errorf("expected but missing: \"%s\"", str)
}
}
}
func TestMoqExplicitPackage(t *testing.T) { func TestMoqExplicitPackage(t *testing.T) {
m, err := New("testpackages/example", "different") m, err := New("testpackages/example", "different")
if err != nil { if err != nil {
@ -69,6 +103,34 @@ func TestMoqExplicitPackage(t *testing.T) {
} }
} }
func TestMoqExplicitPackageWithStaticCheck(t *testing.T) {
m, err := New("testpackages/example", "different")
if err != nil {
t.Fatalf("moq.New: %s", err)
}
var buf bytes.Buffer
err = m.Mock(&buf, "PersonStore")
if err != nil {
t.Errorf("m.Mock: %s", err)
}
s := buf.String()
// assertions of things that should be mentioned
var strs = []string{
"package different",
"var _ example.PersonStore = &PersonStoreMock{}",
"type PersonStoreMock struct",
"CreateFunc func(ctx context.Context, person *example.Person, confirm bool) error",
"GetFunc func(ctx context.Context, id string) (*example.Person, error)",
"func (mock *PersonStoreMock) Create(ctx context.Context, person *example.Person, confirm bool) error",
"func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*example.Person, error)",
}
for _, str := range strs {
if !strings.Contains(s, str) {
t.Errorf("expected but missing: \"%s\"", str)
}
}
}
// TestVeradicArguments tests to ensure variadic work as // TestVeradicArguments tests to ensure variadic work as
// expected. // expected.
// see https://github.com/matryer/moq/issues/5 // see https://github.com/matryer/moq/issues/5

View File

@ -8,6 +8,7 @@ var moqTemplate = `// Code generated by moq; DO NOT EDIT.
// github.com/matryer/moq // github.com/matryer/moq
package {{.PackageName}} package {{.PackageName}}
{{- $sourcePackagePrefix := .SourcePackagePrefix}}
import ( import (
{{- range .Imports }} {{- range .Imports }}
@ -22,6 +23,10 @@ var (
{{- end }} {{- end }}
) )
// Ensure, that {{.InterfaceName}}Mock does implement {{.InterfaceName}}.
// If this is not the case, regenerate this file with moq.
var _ {{$sourcePackagePrefix}}{{.InterfaceName}} = &{{.InterfaceName}}Mock{}
// {{.InterfaceName}}Mock is a mock implementation of {{.InterfaceName}}. // {{.InterfaceName}}Mock is a mock implementation of {{.InterfaceName}}.
// //
// func TestSomethingThatUses{{.InterfaceName}}(t *testing.T) { // func TestSomethingThatUses{{.InterfaceName}}(t *testing.T) {

View File

@ -12,6 +12,10 @@ var (
lockServiceMockUser sync.RWMutex lockServiceMockUser sync.RWMutex
) )
// Ensure, that ServiceMock does implement Service.
// If this is not the case, regenerate this file with moq.
var _ dotimport.Service = &ServiceMock{}
// ServiceMock is a mock implementation of Service. // ServiceMock is a mock implementation of Service.
// //
// func TestSomethingThatUsesService(t *testing.T) { // func TestSomethingThatUsesService(t *testing.T) {

View File

@ -12,6 +12,10 @@ var (
lockServiceMockDoSomething sync.RWMutex lockServiceMockDoSomething sync.RWMutex
) )
// Ensure, that ServiceMock does implement Service.
// If this is not the case, regenerate this file with moq.
var _ Service = &ServiceMock{}
// ServiceMock is a mock implementation of Service. // ServiceMock is a mock implementation of Service.
// //
// func TestSomethingThatUsesService(t *testing.T) { // func TestSomethingThatUsesService(t *testing.T) {

View File

@ -1,3 +1,4 @@
package somerepo package somerepo
// SomeType is some type
type SomeType string type SomeType string