From b3ee86dac056635e7936800645161ea72f9aeb00 Mon Sep 17 00:00:00 2001 From: Ewa Czechowska Date: Wed, 2 Aug 2017 17:44:52 +0000 Subject: [PATCH 1/4] Show file and line number of call during errors This commit makes erroneous calls show their origin. Unexpected calls show where they happened. Missing calls and improper expectations show where they were set up in the tests. This information is printed after each error message in square brackets. This commit was originally: https://github.com/ooesili/mock/commit/76f75d8f9786d8a43801f725ab6f4b065cdc2a15 and a PR for it: https://github.com/golang/mock/pull/25. But there were minor conflicts with master from 2 aug 2017: https://github.com/golang/mock/commit/13f360950a79f5864a972c786a10a50e44b69541, which I fixed. --- gomock/call.go | 24 ++++++++++++++---------- gomock/controller.go | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/gomock/call.go b/gomock/call.go index cc8dfffe..516e54a4 100644 --- a/gomock/call.go +++ b/gomock/call.go @@ -29,6 +29,7 @@ type Call struct { methodType reflect.Type // the type of the method args []Matcher // the args rets []interface{} // the return values (if any) + origin string // file and line number of call setup preReqs []*Call // prerequisite calls @@ -79,8 +80,8 @@ func (c *Call) Do(f interface{}) *Call { func (c *Call) Return(rets ...interface{}) *Call { mt := c.methodType if len(rets) != mt.NumOut() { - c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d", - c.receiver, c.method, len(rets), mt.NumOut()) + c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, len(rets), mt.NumOut(), c.origin) } for i, ret := range rets { if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { @@ -91,8 +92,8 @@ func (c *Call) Return(rets ...interface{}) *Call { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: // ok default: - c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable", - i, c.receiver, c.method, want) + c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", + i, c.receiver, c.method, want, c.origin) } } else if got.AssignableTo(want) { // Assignable type relation. Make the assignment now so that the generated code @@ -101,8 +102,8 @@ func (c *Call) Return(rets ...interface{}) *Call { v.Set(reflect.ValueOf(ret)) rets[i] = v.Interface() } else { - c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v", - i, c.receiver, c.method, got, want) + c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", + i, c.receiver, c.method, got, want, c.origin) } } @@ -125,7 +126,8 @@ func (c *Call) SetArg(n int, value interface{}) *Call { // TODO: This will break on variadic methods. // We will need to check those at invocation time. if n < 0 || n >= mt.NumIn() { - c.t.Fatalf("SetArg(%d, ...) called for a method with %d args", n, mt.NumIn()) + c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", + n, mt.NumIn(), c.origin) } // Permit setting argument through an interface. // In the interface case, we don't (nay, can't) check the type here. @@ -134,12 +136,14 @@ func (c *Call) SetArg(n int, value interface{}) *Call { case reflect.Ptr: dt := at.Elem() if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { - c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v", n, vt, dt) + c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", + n, vt, dt, c.origin) } case reflect.Interface: // nothing to do default: - c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface type %v", n, at) + c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface type %v [%s]", + n, at, c.origin) } c.setArgs[n] = reflect.ValueOf(value) return c @@ -184,7 +188,7 @@ func (c *Call) String() string { args[i] = arg.String() } arguments := strings.Join(args, ", ") - return fmt.Sprintf("%T.%v(%s)", c.receiver, c.method, arguments) + return fmt.Sprintf("%T.%v(%s) [%s]", c.receiver, c.method, arguments, c.origin) } // Tests if the given call matches the expected call. diff --git a/gomock/controller.go b/gomock/controller.go index 6bff78d1..5b3b6c61 100644 --- a/gomock/controller.go +++ b/gomock/controller.go @@ -58,6 +58,7 @@ package gomock import ( "fmt" "reflect" + "runtime" "sync" ) @@ -114,7 +115,8 @@ func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method st ctrl.mu.Lock() defer ctrl.mu.Unlock() - call := &Call{t: ctrl.t, receiver: receiver, method: method, methodType: methodType, args: margs, minCalls: 1, maxCalls: 1} + origin := callerInfo(2) + call := &Call{t: ctrl.t, receiver: receiver, method: method, methodType: methodType, args: margs, origin: origin, minCalls: 1, maxCalls: 1} ctrl.expectedCalls.Add(call) return call @@ -126,7 +128,8 @@ func (ctrl *Controller) Call(receiver interface{}, method string, args ...interf expected := ctrl.expectedCalls.FindMatch(receiver, method, args) if expected == nil { - ctrl.t.Fatalf("no matching expected call: %T.%v(%v)", receiver, method, args) + origin := callerInfo(2) + ctrl.t.Fatalf("no matching expected call: %T.%v(%v) [%s]", receiver, method, args, origin) } // Two things happen here: @@ -181,3 +184,10 @@ func (ctrl *Controller) Finish() { ctrl.t.Fatalf("aborting test due to missing call(s)") } } + +func callerInfo(skip int) string { + if _, file, line, ok := runtime.Caller(skip + 1); ok { + return fmt.Sprintf("%s:%d", file, line) + } + return "unknown file" +} From 253709bcddb8ef202c073458c4f871545af37769 Mon Sep 17 00:00:00 2001 From: Ewa Czechowska Date: Wed, 2 Aug 2017 18:39:11 +0000 Subject: [PATCH 2/4] Return verbose errors why expected method call did not match --- gomock/call.go | 18 ++++-- gomock/callset.go | 23 +++++-- gomock/controller.go | 6 +- gomock/controller_test.go | 131 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 157 insertions(+), 21 deletions(-) diff --git a/gomock/call.go b/gomock/call.go index 516e54a4..ede3bf1b 100644 --- a/gomock/call.go +++ b/gomock/call.go @@ -17,6 +17,7 @@ package gomock import ( "fmt" "reflect" + "strconv" "strings" ) @@ -172,7 +173,7 @@ func (c *Call) After(preReq *Call) *Call { return c } -// Returns true iff the minimum number of calls have been made. +// Returns true if the minimum number of calls have been made. func (c *Call) satisfied() bool { return c.numCalls >= c.minCalls } @@ -192,27 +193,30 @@ func (c *Call) String() string { } // Tests if the given call matches the expected call. -func (c *Call) matches(args []interface{}) bool { +// If yes, returns nil. If no, returns error with message explaining why it does not match. +func (c *Call) matches(args []interface{}) error { if len(args) != len(c.args) { - return false + return fmt.Errorf("Invalid number of arguments of call: %s. Set: %s, while this call takes: %s", + c.origin, strconv.Itoa(len(args)), strconv.Itoa(len(c.args))) } for i, m := range c.args { if !m.Matches(args[i]) { - return false + return fmt.Errorf("The expected argument of index: %s of this call: %s did not match the actual argument.\nActual argument: %s, expected: %v\n", + strconv.Itoa(i), c.origin, m, args[i]) } } // Check that all prerequisite calls have been satisfied. for _, preReqCall := range c.preReqs { if !preReqCall.satisfied() { - return false + return fmt.Errorf("A prerequisite call was not satisfied:\n%v\nshould be called before:\n%v", preReqCall, c) } } - return true + return nil } -// dropPrereqs tells the expected Call to not re-check prerequite calls any +// dropPrereqs tells the expected Call to not re-check prerequisite calls any // longer, and to return its current set. func (c *Call) dropPrereqs() (preReqs []*Call) { preReqs = c.preReqs diff --git a/gomock/callset.go b/gomock/callset.go index 1b7de4c0..b4adac9c 100644 --- a/gomock/callset.go +++ b/gomock/callset.go @@ -14,6 +14,11 @@ package gomock +import ( + "errors" + "fmt" +) + // callSet represents a set of expected calls, indexed by receiver and method // name. type callSet map[interface{}]map[string][]*Call @@ -47,19 +52,20 @@ func (cs callSet) Remove(call *Call) { } } -// FindMatch searches for a matching call. Returns nil if no call matched. -func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) *Call { +// FindMatch searches for a matching call. Returns error with explanation message if no call matched. +func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) { methodMap, ok := cs[receiver] if !ok { - return nil + return nil, errors.New("No expected method calls for that receiver") } calls, ok := methodMap[method] if !ok { - return nil + return nil, fmt.Errorf("No expected calls of the method: %s for that receiver", method) } // Search through the unordered set of calls expected on a method on a // receiver. + callsErrors := "" for _, call := range calls { // A call should not normally still be here if exhausted, // but it can happen if, for instance, .Times(0) was used. @@ -67,10 +73,13 @@ func (cs callSet) FindMatch(receiver interface{}, method string, args []interfac if call.exhausted() { continue } - if call.matches(args) { - return call + err := call.matches(args) + if err != nil { + callsErrors += "\n" + err.Error() + } else { + return call, nil } } - return nil + return nil, fmt.Errorf(callsErrors) } diff --git a/gomock/controller.go b/gomock/controller.go index 5b3b6c61..267632ec 100644 --- a/gomock/controller.go +++ b/gomock/controller.go @@ -126,10 +126,10 @@ func (ctrl *Controller) Call(receiver interface{}, method string, args ...interf ctrl.mu.Lock() defer ctrl.mu.Unlock() - expected := ctrl.expectedCalls.FindMatch(receiver, method, args) - if expected == nil { + expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) + if err != nil { origin := callerInfo(2) - ctrl.t.Fatalf("no matching expected call: %T.%v(%v) [%s]", receiver, method, args, origin) + ctrl.t.Fatalf("no matching expected call: %T.%v(%v) [%s]\n%s", receiver, method, args, origin, err) } // Two things happen here: diff --git a/gomock/controller_test.go b/gomock/controller_test.go index 57f79572..f7e7b292 100644 --- a/gomock/controller_test.go +++ b/gomock/controller_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/golang/mock/gomock" + "strings" ) type ErrorReporter struct { @@ -53,7 +54,7 @@ func (e *ErrorReporter) assertFail(msg string) { } // Use to check that code triggers a fatal test failure. -func (e *ErrorReporter) assertFatal(fn func()) { +func (e *ErrorReporter) assertFatal(fn func(), expectedErrMsgs ...string) { defer func() { err := recover() if err == nil { @@ -66,6 +67,18 @@ func (e *ErrorReporter) assertFatal(fn func()) { e.t.Error("Expected fatal failure, but got a", actual) } else if token, ok := err.(*struct{}); ok && token == &e.fatalToken { // This is okay - the panic is from Fatalf(). + if expectedErrMsgs != nil { + // assert that the actual error message + // contains expectedErrMsgs + + // check the last actualErrMsg, because the previous messages come from previous errors + actualErrMsg := e.log[len(e.log)-1] + for _, expectedErrMsg := range expectedErrMsgs { + if !strings.Contains(actualErrMsg, expectedErrMsg) { + e.t.Errorf("Expected the actual error message:\n'%s'\nto contain expected error message:\n'%s'\n", actualErrMsg, expectedErrMsg) + } + } + } return } else { // Some other panic. @@ -119,6 +132,21 @@ func (s *Subject) BarMethod(arg string) int { return 0 } +// A type purely for ActOnTestStructMethod +type TestStruct struct { + Number int + Message string +} + +func (s *Subject) ActOnTestStructMethod(arg TestStruct, arg1 int) int { + return 0 +} + +// Without this method the string representation of a TestStruct object would be e.g. {%!!(MISSING)s(int=123) no message} +func (tStruct TestStruct) String() string { + return fmt.Sprintf("{%v %s}", tStruct.Number, tStruct.Message) +} + func assertEqual(t *testing.T, expected interface{}, actual interface{}) { if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %+v, but got %+v", expected, actual) @@ -140,6 +168,30 @@ func TestNoCalls(t *testing.T) { reporter.assertPass("No calls expected or made.") } +func TestNoRecordedCallsForAReceiver(t *testing.T) { + reporter, ctrl := createFixtures(t) + subject := new(Subject) + + reporter.assertFatal(func() { + ctrl.Call(subject, "NotRecordedMethod", "argument") + }, "No expected method calls for that receiver") + ctrl.Finish() +} + +func TestNoRecordedMatchingMethodNameForAReceiver(t *testing.T) { + reporter, ctrl := createFixtures(t) + subject := new(Subject) + + ctrl.RecordCall(subject, "FooMethod", "argument") + reporter.assertFatal(func() { + ctrl.Call(subject, "NotRecordedMethod", "argument") + }, "No expected calls of the method: NotRecordedMethod for that receiver") + reporter.assertFatal(func() { + // The expected call wasn't made. + ctrl.Finish() + }) +} + func TestExpectedMethodCall(t *testing.T) { reporter, ctrl := createFixtures(t) subject := new(Subject) @@ -187,11 +239,68 @@ func TestUnexpectedArgCount(t *testing.T) { reporter.assertFatal(func() { // This call is made with the wrong number of arguments... ctrl.Call(subject, "FooMethod", "argument", "extra_argument") - }) + }, "no matching expected call", "Invalid number of arguments of call", "Set: 2, while this call takes: 1") reporter.assertFatal(func() { // ... so is this. ctrl.Call(subject, "FooMethod") + }, "no matching expected call", "Invalid number of arguments of call", "Set: 0, while this call takes: 1") + reporter.assertFatal(func() { + // The expected call wasn't made. + ctrl.Finish() }) +} + +func TestExpectedMethodCall_CustomStruct(t *testing.T) { + reporter, ctrl := createFixtures(t) + subject := new(Subject) + + expectedArg0 := TestStruct{Number: 123, Message: "hello"} + ctrl.RecordCall(subject, "ActOnTestStructMethod", expectedArg0, 15) + ctrl.Call(subject, "ActOnTestStructMethod", expectedArg0, 15) + + reporter.assertPass("Expected method call made.") +} + +func TestUnexpectedArgValue_FirstArg(t *testing.T) { + reporter, ctrl := createFixtures(t) + defer reporter.recoverUnexpectedFatal() + subject := new(Subject) + + expectedArg0 := TestStruct{Number: 123, Message: "hello"} + ctrl.RecordCall(subject, "ActOnTestStructMethod", expectedArg0, 15) + + reporter.assertFatal(func() { + // the method argument (of TestStruct type) has 1 unexpected value (for the Number field) + ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 123, Message: "no message"}, 15) + }, "no matching expected call", "The expected argument of index: 0 of this call", + "Actual argument: is equal to {123 hello}, expected: {123 no message}") + + reporter.assertFatal(func() { + // the method argument (of TestStruct type) has 2 unexpected values (for both fields) + ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 11, Message: "no message"}, 15) + }, "no matching expected call", "The expected argument of index: 0 of this call", + "Actual argument: is equal to {123 hello}, expected: {11 no message}") + + reporter.assertFatal(func() { + // The expected call wasn't made. + ctrl.Finish() + }) +} + +func TestUnexpectedArgValue_SecondtArg(t *testing.T) { + reporter, ctrl := createFixtures(t) + defer reporter.recoverUnexpectedFatal() + subject := new(Subject) + + expectedArg0 := TestStruct{Number: 123, Message: "hello"} + ctrl.RecordCall(subject, "ActOnTestStructMethod", expectedArg0, 15) + + reporter.assertFatal(func() { + // the method argument (of TestStruct type) has 1 (Number) unexpected value + ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 123, Message: "hello"}, 3) + }, "no matching expected call", "The expected argument of index: 1 of this call", + "Actual argument: is equal to 15, expected: 3") + reporter.assertFatal(func() { // The expected call wasn't made. ctrl.Finish() @@ -394,10 +503,10 @@ func TestOrderedCallsInCorrect(t *testing.T) { ctrl.Call(subjectOne, "FooMethod", "1") reporter.assertFatal(func() { ctrl.Call(subjectTwo, "BarMethod", "3") - }) + }, "no matching expected call", "Subject.BarMethod([3])") } -// Test that calls that are prerequites to other calls but have maxCalls > +// Test that calls that are prerequisites to other calls but have maxCalls > // minCalls are removed from the expected call set. func TestOrderedCallsWithPreReqMaxUnbounded(t *testing.T) { reporter, ctrl, subjectOne, subjectTwo := commonTestOrderedCalls(t) @@ -418,6 +527,20 @@ func TestOrderedCallsWithPreReqMaxUnbounded(t *testing.T) { }) } +func TestPrerequisiteCallNotSatisfied(t *testing.T) { + reporter, ctrl := createFixtures(t) + subject := new(Subject) + + firstCall := ctrl.RecordCall(subject, "FooMethod", "1") + secondCall := ctrl.RecordCall(subject, "FooMethod", "2") + secondCall.After(firstCall) + + reporter.assertFatal(func() { + // FooMethod(1) should be called before FooMethod(2), but it wasn't + ctrl.Call(subject, "FooMethod", "2") + }, "A prerequisite call was not satisfied") +} + func TestCallAfterLoopPanic(t *testing.T) { _, ctrl := createFixtures(t) From 4187d4d04aa043124750c9250259ceafdc5f7380 Mon Sep 17 00:00:00 2001 From: Ewa Czechowska Date: Thu, 3 Aug 2017 16:04:04 +0000 Subject: [PATCH 3/4] Do not surround call origin with [] brackets to let Gogland render it as a clickable link --- gomock/call.go | 2 +- gomock/controller.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gomock/call.go b/gomock/call.go index ede3bf1b..bad1fe6e 100644 --- a/gomock/call.go +++ b/gomock/call.go @@ -189,7 +189,7 @@ func (c *Call) String() string { args[i] = arg.String() } arguments := strings.Join(args, ", ") - return fmt.Sprintf("%T.%v(%s) [%s]", c.receiver, c.method, arguments, c.origin) + return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) } // Tests if the given call matches the expected call. diff --git a/gomock/controller.go b/gomock/controller.go index 267632ec..5b6e9320 100644 --- a/gomock/controller.go +++ b/gomock/controller.go @@ -129,7 +129,7 @@ func (ctrl *Controller) Call(receiver interface{}, method string, args ...interf expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) if err != nil { origin := callerInfo(2) - ctrl.t.Fatalf("no matching expected call: %T.%v(%v) [%s]\n%s", receiver, method, args, origin, err) + ctrl.t.Fatalf("no matching expected call: %T.%v(%v) %s\n%s", receiver, method, args, origin, err) } // Two things happen here: From d35f0d93b94b243ab17fead94dc82edcbc2340a2 Mon Sep 17 00:00:00 2001 From: Ewa Czechowska Date: Sun, 6 Aug 2017 14:11:41 +0200 Subject: [PATCH 4/4] Error messages more clear to read for end user --- gomock/call.go | 11 +++++---- gomock/callset.go | 5 ++-- gomock/controller.go | 2 +- gomock/controller_test.go | 49 +++++++++++++-------------------------- 4 files changed, 26 insertions(+), 41 deletions(-) diff --git a/gomock/call.go b/gomock/call.go index bad1fe6e..9f3ae9c8 100644 --- a/gomock/call.go +++ b/gomock/call.go @@ -163,7 +163,7 @@ func (c *Call) isPreReq(other *Call) bool { // After declares that the call may only match after preReq has been exhausted. func (c *Call) After(preReq *Call) *Call { if c == preReq { - c.t.Fatalf("A call isn't allowed to be it's own prerequisite") + c.t.Fatalf("A call isn't allowed to be its own prerequisite") } if preReq.isPreReq(c) { c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) @@ -196,20 +196,21 @@ func (c *Call) String() string { // If yes, returns nil. If no, returns error with message explaining why it does not match. func (c *Call) matches(args []interface{}) error { if len(args) != len(c.args) { - return fmt.Errorf("Invalid number of arguments of call: %s. Set: %s, while this call takes: %s", + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %s, want: %s", c.origin, strconv.Itoa(len(args)), strconv.Itoa(len(c.args))) } for i, m := range c.args { if !m.Matches(args[i]) { - return fmt.Errorf("The expected argument of index: %s of this call: %s did not match the actual argument.\nActual argument: %s, expected: %v\n", - strconv.Itoa(i), c.origin, m, args[i]) + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v\n", + c.origin, strconv.Itoa(i), args[i], m) } } // Check that all prerequisite calls have been satisfied. for _, preReqCall := range c.preReqs { if !preReqCall.satisfied() { - return fmt.Errorf("A prerequisite call was not satisfied:\n%v\nshould be called before:\n%v", preReqCall, c) + return fmt.Errorf("Expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", + c.origin, preReqCall, c) } } diff --git a/gomock/callset.go b/gomock/callset.go index b4adac9c..a88a172f 100644 --- a/gomock/callset.go +++ b/gomock/callset.go @@ -56,11 +56,11 @@ func (cs callSet) Remove(call *Call) { func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) { methodMap, ok := cs[receiver] if !ok { - return nil, errors.New("No expected method calls for that receiver") + return nil, errors.New("there are no expected method calls for that receiver") } calls, ok := methodMap[method] if !ok { - return nil, fmt.Errorf("No expected calls of the method: %s for that receiver", method) + return nil, fmt.Errorf("there are no expected calls of the method: %s for that receiver", method) } // Search through the unordered set of calls expected on a method on a @@ -71,6 +71,7 @@ func (cs callSet) FindMatch(receiver interface{}, method string, args []interfac // but it can happen if, for instance, .Times(0) was used. // Pretend the call doesn't match. if call.exhausted() { + callsErrors += "\nThe call was exhausted." continue } err := call.matches(args) diff --git a/gomock/controller.go b/gomock/controller.go index 5b6e9320..d06dfbbe 100644 --- a/gomock/controller.go +++ b/gomock/controller.go @@ -129,7 +129,7 @@ func (ctrl *Controller) Call(receiver interface{}, method string, args ...interf expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) if err != nil { origin := callerInfo(2) - ctrl.t.Fatalf("no matching expected call: %T.%v(%v) %s\n%s", receiver, method, args, origin, err) + ctrl.t.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err) } // Two things happen here: diff --git a/gomock/controller_test.go b/gomock/controller_test.go index f7e7b292..d4e3bdc3 100644 --- a/gomock/controller_test.go +++ b/gomock/controller_test.go @@ -75,7 +75,7 @@ func (e *ErrorReporter) assertFatal(fn func(), expectedErrMsgs ...string) { actualErrMsg := e.log[len(e.log)-1] for _, expectedErrMsg := range expectedErrMsgs { if !strings.Contains(actualErrMsg, expectedErrMsg) { - e.t.Errorf("Expected the actual error message:\n'%s'\nto contain expected error message:\n'%s'\n", actualErrMsg, expectedErrMsg) + e.t.Errorf("Error message:\ngot: %q\nwant to contain: %q\n", actualErrMsg, expectedErrMsg) } } } @@ -142,11 +142,6 @@ func (s *Subject) ActOnTestStructMethod(arg TestStruct, arg1 int) int { return 0 } -// Without this method the string representation of a TestStruct object would be e.g. {%!!(MISSING)s(int=123) no message} -func (tStruct TestStruct) String() string { - return fmt.Sprintf("{%v %s}", tStruct.Number, tStruct.Message) -} - func assertEqual(t *testing.T, expected interface{}, actual interface{}) { if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %+v, but got %+v", expected, actual) @@ -174,7 +169,7 @@ func TestNoRecordedCallsForAReceiver(t *testing.T) { reporter.assertFatal(func() { ctrl.Call(subject, "NotRecordedMethod", "argument") - }, "No expected method calls for that receiver") + }, "Unexpected call to", "there are no expected method calls for that receiver") ctrl.Finish() } @@ -185,13 +180,14 @@ func TestNoRecordedMatchingMethodNameForAReceiver(t *testing.T) { ctrl.RecordCall(subject, "FooMethod", "argument") reporter.assertFatal(func() { ctrl.Call(subject, "NotRecordedMethod", "argument") - }, "No expected calls of the method: NotRecordedMethod for that receiver") + }, "Unexpected call to", "there are no expected calls of the method: NotRecordedMethod for that receiver") reporter.assertFatal(func() { // The expected call wasn't made. ctrl.Finish() }) } +// This tests that a call with an arguments of some primitive type matches a recorded call. func TestExpectedMethodCall(t *testing.T) { reporter, ctrl := createFixtures(t) subject := new(Subject) @@ -239,17 +235,18 @@ func TestUnexpectedArgCount(t *testing.T) { reporter.assertFatal(func() { // This call is made with the wrong number of arguments... ctrl.Call(subject, "FooMethod", "argument", "extra_argument") - }, "no matching expected call", "Invalid number of arguments of call", "Set: 2, while this call takes: 1") + }, "Unexpected call to", "wrong number of arguments", "Got: 2, want: 1") reporter.assertFatal(func() { // ... so is this. ctrl.Call(subject, "FooMethod") - }, "no matching expected call", "Invalid number of arguments of call", "Set: 0, while this call takes: 1") + }, "Unexpected call to", "wrong number of arguments", "Got: 0, want: 1") reporter.assertFatal(func() { // The expected call wasn't made. ctrl.Finish() }) } +// This tests that a call with complex arguments (a struct and some primitive type) matches a recorded call. func TestExpectedMethodCall_CustomStruct(t *testing.T) { reporter, ctrl := createFixtures(t) subject := new(Subject) @@ -270,16 +267,16 @@ func TestUnexpectedArgValue_FirstArg(t *testing.T) { ctrl.RecordCall(subject, "ActOnTestStructMethod", expectedArg0, 15) reporter.assertFatal(func() { - // the method argument (of TestStruct type) has 1 unexpected value (for the Number field) + // the method argument (of TestStruct type) has 1 unexpected value (for the Message field) ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 123, Message: "no message"}, 15) - }, "no matching expected call", "The expected argument of index: 0 of this call", - "Actual argument: is equal to {123 hello}, expected: {123 no message}") + }, "Unexpected call to", "doesn't match the argument at index 0", + "Got: {123 no message}\nWant: is equal to {123 hello}") reporter.assertFatal(func() { // the method argument (of TestStruct type) has 2 unexpected values (for both fields) ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 11, Message: "no message"}, 15) - }, "no matching expected call", "The expected argument of index: 0 of this call", - "Actual argument: is equal to {123 hello}, expected: {11 no message}") + }, "Unexpected call to", "doesn't match the argument at index 0", + "Got: {11 no message}\nWant: is equal to {123 hello}") reporter.assertFatal(func() { // The expected call wasn't made. @@ -296,10 +293,9 @@ func TestUnexpectedArgValue_SecondtArg(t *testing.T) { ctrl.RecordCall(subject, "ActOnTestStructMethod", expectedArg0, 15) reporter.assertFatal(func() { - // the method argument (of TestStruct type) has 1 (Number) unexpected value ctrl.Call(subject, "ActOnTestStructMethod", TestStruct{Number: 123, Message: "hello"}, 3) - }, "no matching expected call", "The expected argument of index: 1 of this call", - "Actual argument: is equal to 15, expected: 3") + }, "Unexpected call to", "doesn't match the argument at index 1", + "Got: 3\nWant: is equal to 15") reporter.assertFatal(func() { // The expected call wasn't made. @@ -502,8 +498,9 @@ func TestOrderedCallsInCorrect(t *testing.T) { ctrl.Call(subjectOne, "FooMethod", "1") reporter.assertFatal(func() { + // FooMethod(2) should be called before BarMethod(3) ctrl.Call(subjectTwo, "BarMethod", "3") - }, "no matching expected call", "Subject.BarMethod([3])") + }, "Unexpected call to", "Subject.BarMethod([3])", "doesn't have a prerequisite call satisfied") } // Test that calls that are prerequisites to other calls but have maxCalls > @@ -527,20 +524,6 @@ func TestOrderedCallsWithPreReqMaxUnbounded(t *testing.T) { }) } -func TestPrerequisiteCallNotSatisfied(t *testing.T) { - reporter, ctrl := createFixtures(t) - subject := new(Subject) - - firstCall := ctrl.RecordCall(subject, "FooMethod", "1") - secondCall := ctrl.RecordCall(subject, "FooMethod", "2") - secondCall.After(firstCall) - - reporter.assertFatal(func() { - // FooMethod(1) should be called before FooMethod(2), but it wasn't - ctrl.Call(subject, "FooMethod", "2") - }, "A prerequisite call was not satisfied") -} - func TestCallAfterLoopPanic(t *testing.T) { _, ctrl := createFixtures(t)