From 91d4b5c4fd7c91ee7630f68dc0e0dffc5d193b44 Mon Sep 17 00:00:00 2001 From: Cole Wippern Date: Sat, 25 Jul 2020 19:32:24 -0700 Subject: [PATCH] Add tests for various Do/DoAndReturn calls (#430) --- gomock/call_test.go | 537 ++++++++++++++++++++++++++++++++++++++++++++ sample/user_test.go | 38 +++- 2 files changed, 574 insertions(+), 1 deletion(-) diff --git a/gomock/call_test.go b/gomock/call_test.go index 3a8315b3..e90a5d67 100644 --- a/gomock/call_test.go +++ b/gomock/call_test.go @@ -1,9 +1,47 @@ +// Copyright 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package gomock import ( + "fmt" + "reflect" "testing" ) +type foo struct{} + +func (f foo) String() string { + return "meow" +} + +type a struct { + name string +} + +func (testObj a) Name() string { + return testObj.name +} + +type b struct { + foo string +} + +func (testObj b) Foo() string { + return testObj.foo +} + type mockTestReporter struct { errorCalls int fatalCalls int @@ -49,3 +87,502 @@ func TestCall_After(t *testing.T) { } }) } + +func prepareDoCall(doFunc, callFunc interface{}) *Call { + tr := &mockTestReporter{} + + c := &Call{ + t: tr, + methodType: reflect.TypeOf(callFunc), + } + + c.Do(doFunc) + + return c +} + +func prepareDoAndReturnCall(doFunc, callFunc interface{}) *Call { + tr := &mockTestReporter{} + + c := &Call{ + t: tr, + methodType: reflect.TypeOf(callFunc), + } + + c.DoAndReturn(doFunc) + + return c +} + +type testCase struct { + description string + doFunc interface{} + callFunc interface{} + args []interface{} + expectPanic bool +} + +var testCases []testCase = []testCase{ + { + description: "argument to Do is not a function", + doFunc: "meow", + callFunc: func(x int, y int) { + return + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "argument to Do is not a function", + doFunc: "meow", + callFunc: func(x int, y int) bool { + return true + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "number of args for Do func don't match Call func", + doFunc: func(x int) { + return + }, + callFunc: func(x int, y int) { + return + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "number of args for Do func don't match Call func", + doFunc: func(x int) bool { + return true + }, + callFunc: func(x int, y int) bool { + return true + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "arg type for Do func incompatible with Call func", + doFunc: func(x int) { + return + }, + callFunc: func(x string) { + return + }, + args: []interface{}{"meow"}, + expectPanic: true, + }, { + description: "arg type for Do func incompatible with Call func", + doFunc: func(x int) bool { + return true + }, + callFunc: func(x string) bool { + return true + }, + args: []interface{}{"meow"}, + expectPanic: true, + }, { + description: "Do func(int) Call func(int)", + doFunc: func(x int) { + return + }, + callFunc: func(x int) { + return + }, + args: []interface{}{0}, + }, { + description: "Do func(int) Call func(interface{})", + doFunc: func(x int) { + return + }, + callFunc: func(x interface{}) { + return + }, + args: []interface{}{0}, + }, { + description: "Do func(int) bool Call func(int) bool", + doFunc: func(x int) bool { + return true + }, + callFunc: func(x int) bool { + return true + }, + args: []interface{}{0}, + }, { + description: "Do func(int) bool Call func(interface{}) bool", + doFunc: func(x int) bool { + return true + }, + callFunc: func(x interface{}) bool { + return true + }, + args: []interface{}{0}, + }, { + description: "Do func(string) Call func([]byte)", + doFunc: func(x string) { + return + }, + callFunc: func(x []byte) { + return + }, + args: []interface{}{[]byte("meow")}, + expectPanic: true, + }, { + description: "Do func(string) bool Call func([]byte) bool", + doFunc: func(x string) bool { + return true + }, + callFunc: func(x []byte) bool { + return true + }, + args: []interface{}{[]byte("meow")}, + expectPanic: true, + }, { + description: "Do func(map[int]string) Call func(map[interface{}]int)", + doFunc: func(x map[int]string) { + return + }, + callFunc: func(x map[interface{}]int) { + return + }, + args: []interface{}{map[interface{}]int{"meow": 0}}, + expectPanic: true, + }, { + description: "Do func(map[int]string) Call func(map[interface{}]interface{})", + doFunc: func(x map[int]string) { + return + }, + callFunc: func(x map[interface{}]interface{}) { + return + }, + args: []interface{}{map[interface{}]interface{}{"meow": "meow"}}, + expectPanic: true, + }, { + description: "Do func(map[int]string) bool Call func(map[interface{}]int) bool", + doFunc: func(x map[int]string) bool { + return true + }, + callFunc: func(x map[interface{}]int) bool { + return true + }, + args: []interface{}{map[interface{}]int{"meow": 0}}, + expectPanic: true, + }, { + description: "Do func(map[int]string) bool Call func(map[interface{}]interface{}) bool", + doFunc: func(x map[int]string) bool { + return true + }, + callFunc: func(x map[interface{}]interface{}) bool { + return true + }, + args: []interface{}{map[interface{}]interface{}{"meow": "meow"}}, + expectPanic: true, + }, { + description: "Do func([]string) Call func([]interface{})", + doFunc: func(x []string) { + return + }, + callFunc: func(x []interface{}) { + return + }, + args: []interface{}{[]interface{}{0}}, + expectPanic: true, + }, { + description: "Do func([]string) Call func([]int)", + doFunc: func(x []string) { + return + }, + callFunc: func(x []int) { + return + }, + args: []interface{}{[]int{0, 1}}, + expectPanic: true, + }, { + description: "Do func([]int) Call func([]int)", + doFunc: func(x []int) { + return + }, + callFunc: func(x []int) { + return + }, + args: []interface{}{[]int{0, 1}}, + }, { + description: "Do func([]int) Call func([]interface{})", + doFunc: func(x []int) { + return + }, + callFunc: func(x []interface{}) { + return + }, + args: []interface{}{[]interface{}{0}}, + expectPanic: true, + }, { + description: "Do func([]int) Call func(...interface{})", + doFunc: func(x []int) { + return + }, + callFunc: func(x ...interface{}) { + return + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "Do func([]int) Call func(...int)", + doFunc: func(x []int) { + return + }, + callFunc: func(x ...int) { + return + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "Do func([]string) bool Call func([]interface{}) bool", + doFunc: func(x []string) bool { + return true + }, + callFunc: func(x []interface{}) bool { + return true + }, + args: []interface{}{[]interface{}{0}}, + expectPanic: true, + }, { + description: "Do func([]string) bool Call func([]int) bool", + doFunc: func(x []string) bool { + return true + }, + callFunc: func(x []int) bool { + return true + }, + args: []interface{}{[]int{0, 1}}, + expectPanic: true, + }, { + description: "Do func([]int) bool Call func([]int) bool", + doFunc: func(x []int) bool { + return true + }, + callFunc: func(x []int) bool { + return true + }, + args: []interface{}{[]int{0, 1}}, + }, { + description: "Do func([]int) bool Call func([]interface{}) bool", + doFunc: func(x []int) bool { + return true + }, + callFunc: func(x []interface{}) bool { + return true + }, + args: []interface{}{[]interface{}{0}}, + expectPanic: true, + }, { + description: "Do func([]int) bool Call func(...interface{}) bool", + doFunc: func(x []int) bool { + return true + }, + callFunc: func(x ...interface{}) bool { + return true + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "Do func([]int) bool Call func(...int) bool", + doFunc: func(x []int) bool { + return true + }, + callFunc: func(x ...int) bool { + return true + }, + args: []interface{}{0, 1}, + expectPanic: true, + }, { + description: "Do func(...int) Call func([]int)", + doFunc: func(x ...int) { + return + }, + callFunc: func(x []int) { + return + }, + args: []interface{}{[]int{0, 1}}, + expectPanic: true, + }, { + description: "Do func(...int) Call func([]interface{})", + doFunc: func(x ...int) { + return + }, + callFunc: func(x []interface{}) { + return + }, + args: []interface{}{[]interface{}{0, 1}}, + expectPanic: true, + }, { + description: "Do func(...int) Call func(...interface{})", + doFunc: func(x ...int) { + return + }, + callFunc: func(x ...interface{}) { + return + }, + args: []interface{}{0, 1}, + }, { + description: "Do func(...int) bool Call func(...int) bool", + doFunc: func(x ...int) bool { + return true + }, + callFunc: func(x ...int) bool { + return true + }, + args: []interface{}{0, 1}, + }, { + description: "Do func(...int) bool Call func([]int) bool", + doFunc: func(x ...int) bool { + return true + }, + callFunc: func(x []int) bool { + return true + }, + args: []interface{}{[]int{0, 1}}, + expectPanic: true, + }, { + description: "Do func(...int) bool Call func([]interface{}) bool", + doFunc: func(x ...int) bool { + return true + }, + callFunc: func(x []interface{}) bool { + return true + }, + args: []interface{}{[]interface{}{0, 1}}, + expectPanic: true, + }, { + description: "Do func(...int) bool Call func(...interface{}) bool", + doFunc: func(x ...int) bool { + return true + }, + callFunc: func(x ...interface{}) bool { + return true + }, + args: []interface{}{0, 1}, + }, { + description: "Do func(...int) Call func(...int)", + doFunc: func(x ...int) { + return + }, + callFunc: func(x ...int) { + return + }, + args: []interface{}{0, 1}, + }, { + description: "Do func(foo); foo implements interface X Call func(interface X)", + doFunc: func(x foo) { + return + }, + callFunc: func(x fmt.Stringer) { + return + }, + args: []interface{}{foo{}}, + }, { + description: "Do func(b); b does not implement interface X Call func(interface X)", + doFunc: func(x b) { + return + }, + callFunc: func(x fmt.Stringer) { + return + }, + args: []interface{}{foo{}}, + expectPanic: true, + }, { + description: "Do func(b) Call func(a); a and b are not aliases", + doFunc: func(x b) { + return + }, + callFunc: func(x a) { + return + }, + args: []interface{}{a{}}, + expectPanic: true, + }, { + description: "Do func(foo) bool; foo implements interface X Call func(interface X) bool", + doFunc: func(x foo) bool { + return true + }, + callFunc: func(x fmt.Stringer) bool { + return true + }, + args: []interface{}{foo{}}, + }, { + description: "Do func(b) bool; b does not implement interface X Call func(interface X) bool", + doFunc: func(x b) bool { + return true + }, + callFunc: func(x fmt.Stringer) bool { + return true + }, + args: []interface{}{foo{}}, + expectPanic: true, + }, { + description: "Do func(b) bool Call func(a) bool; a and b are not aliases", + doFunc: func(x b) bool { + return true + }, + callFunc: func(x a) bool { + return true + }, + args: []interface{}{a{}}, + expectPanic: true, + }, { + description: "Do func(bool) b Call func(bool) a; a and b are not aliases", + doFunc: func(x bool) b { + return b{} + }, + callFunc: func(x bool) a { + return a{} + }, + args: []interface{}{true}, + }, +} + +func TestCall_Do(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + c := prepareDoCall(tc.doFunc, tc.callFunc) + + if len(c.actions) != 1 { + t.Errorf("expected %d actions but got %d", 1, len(c.actions)) + } + + action := c.actions[0] + + if tc.expectPanic { + defer func() { + if r := recover(); r == nil { + t.Error("expected Do to panic") + } + }() + } + + action(tc.args) + }) + } +} + +func TestCall_DoAndReturn(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + c := prepareDoAndReturnCall(tc.doFunc, tc.callFunc) + + if len(c.actions) != 1 { + t.Errorf("expected %d actions but got %d", 1, len(c.actions)) + } + + action := c.actions[0] + + if tc.expectPanic { + defer func() { + if r := recover(); r == nil { + t.Error("expected DoAndReturn to panic") + } + }() + } + + action(tc.args) + }) + } +} diff --git a/sample/user_test.go b/sample/user_test.go index d1de99cd..16f287a1 100644 --- a/sample/user_test.go +++ b/sample/user_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/golang/mock/sample" + user "github.com/golang/mock/sample" "github.com/golang/mock/sample/imp1" mock_user "github.com/golang/mock/sample/mock_user" ) @@ -159,3 +159,39 @@ func TestExpectTrueNil(t *testing.T) { mockIndex.EXPECT().Ptr(nil) // this nil is a nil interface{} mockIndex.Ptr(nil) // this nil is a nil *int } + +func TestDoAndReturnSignature(t *testing.T) { + t.Run("wrong number of return args", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockIndex := mock_user.NewMockIndex(ctrl) + + mockIndex.EXPECT().Slice(gomock.Any(), gomock.Any()).DoAndReturn( + func(_ []int, _ []byte) { + return + }) + + defer func() { + if r := recover(); r == nil { + t.Error("expected panic") + } + }() + + mockIndex.Slice([]int{0}, []byte("meow")) + }) + + t.Run("wrong type of return arg", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockIndex := mock_user.NewMockIndex(ctrl) + + mockIndex.EXPECT().Slice(gomock.Any(), gomock.Any()).DoAndReturn( + func(_ []int, _ []byte) bool { + return true + }) + + mockIndex.Slice([]int{0}, []byte("meow")) + }) +}