From b21592468b0e5f719c036e3c10426d406d1ba888 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Mon, 4 Jun 2018 14:22:28 +0200 Subject: [PATCH] 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. --- example/mockpersonstore_test.go | 4 ++ generate/generated.go | 4 ++ main.go | 2 +- pkg/moq/moq.go | 11 +++- pkg/moq/moq_test.go | 62 +++++++++++++++++++ pkg/moq/template.go | 5 ++ .../dotimport/service_moq_test.go | 4 ++ .../gogenvendoring/user/user_moq_test.go | 4 ++ pkg/vendor/github.com/matryer/somerepo/yep.go | 1 + 9 files changed, 93 insertions(+), 4 deletions(-) diff --git a/example/mockpersonstore_test.go b/example/mockpersonstore_test.go index a0880b5..813d54a 100755 --- a/example/mockpersonstore_test.go +++ b/example/mockpersonstore_test.go @@ -13,6 +13,10 @@ var ( 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. // // func TestSomethingThatUsesPersonStore(t *testing.T) { diff --git a/generate/generated.go b/generate/generated.go index e6ba29a..5f0c151 100644 --- a/generate/generated.go +++ b/generate/generated.go @@ -13,6 +13,10 @@ var ( 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. // // func TestSomethingThatUsesMyInterface(t *testing.T) { diff --git a/main.go b/main.go index 04cd2c8..e9a17d9 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,12 @@ package main import ( "bytes" + "errors" "flag" "fmt" "io" "io/ioutil" "os" - "errors" "github.com/matryer/moq/pkg/moq" ) diff --git a/pkg/moq/moq.go b/pkg/moq/moq.go index d2ad06e..5642422 100644 --- a/pkg/moq/moq.go +++ b/pkg/moq/moq.go @@ -171,6 +171,10 @@ func (m *Mocker) Mock(w io.Writer, name ...string) error { doc.Imports = append(doc.Imports, stripVendorPath(pkgToImport)) } + if tpkg.Name() != m.pkgName { + doc.SourcePackagePrefix = tpkg.Name() + "." + } + var buf bytes.Buffer err = m.tmpl.Execute(&buf, doc) if err != nil { @@ -249,9 +253,10 @@ func pkgInfoFromPath(src string) (*loader.PackageInfo, error) { } type doc struct { - PackageName string - Objects []obj - Imports []string + PackageName string + SourcePackagePrefix string + Objects []obj + Imports []string } type obj struct { diff --git a/pkg/moq/moq_test.go b/pkg/moq/moq_test.go index 4322869..6766d4d 100644 --- a/pkg/moq/moq_test.go +++ b/pkg/moq/moq_test.go @@ -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) { m, err := New("testpackages/example", "different") 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 // expected. // see https://github.com/matryer/moq/issues/5 diff --git a/pkg/moq/template.go b/pkg/moq/template.go index 31a6c60..a3b1984 100644 --- a/pkg/moq/template.go +++ b/pkg/moq/template.go @@ -8,6 +8,7 @@ var moqTemplate = `// Code generated by moq; DO NOT EDIT. // github.com/matryer/moq package {{.PackageName}} +{{- $sourcePackagePrefix := .SourcePackagePrefix}} import ( {{- range .Imports }} @@ -22,6 +23,10 @@ var ( {{- 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}}. // // func TestSomethingThatUses{{.InterfaceName}}(t *testing.T) { diff --git a/pkg/moq/testpackages/dotimport/service_moq_test.go b/pkg/moq/testpackages/dotimport/service_moq_test.go index c8aeb86..87ee14e 100755 --- a/pkg/moq/testpackages/dotimport/service_moq_test.go +++ b/pkg/moq/testpackages/dotimport/service_moq_test.go @@ -12,6 +12,10 @@ var ( 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. // // func TestSomethingThatUsesService(t *testing.T) { diff --git a/pkg/moq/testpackages/gogenvendoring/user/user_moq_test.go b/pkg/moq/testpackages/gogenvendoring/user/user_moq_test.go index 109b691..95fd2d7 100644 --- a/pkg/moq/testpackages/gogenvendoring/user/user_moq_test.go +++ b/pkg/moq/testpackages/gogenvendoring/user/user_moq_test.go @@ -12,6 +12,10 @@ var ( 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. // // func TestSomethingThatUsesService(t *testing.T) { diff --git a/pkg/vendor/github.com/matryer/somerepo/yep.go b/pkg/vendor/github.com/matryer/somerepo/yep.go index 8f4ff9a..b580e91 100644 --- a/pkg/vendor/github.com/matryer/somerepo/yep.go +++ b/pkg/vendor/github.com/matryer/somerepo/yep.go @@ -1,3 +1,4 @@ package somerepo +// SomeType is some type type SomeType string