From 2fab6dffcf101c5a79fc8f3cd67a3541432db5fd Mon Sep 17 00:00:00 2001 From: Ruan Moolman Date: Tue, 28 Jun 2022 13:14:39 +0200 Subject: [PATCH 1/4] Add WithinTimeRange method (#1188) * Add WithinTimeRange method * Run ./.ci.generate.sh * Rename WithinTimeRange to WithinRange * Rename WithinRange expected parameter to actual * Capitalise start parameter at start of error message * Improve WithinRange example --- assert/assertion_format.go | 10 ++++++++++ assert/assertion_forward.go | 20 ++++++++++++++++++++ assert/assertions.go | 21 +++++++++++++++++++++ assert/assertions_test.go | 19 +++++++++++++++++++ require/require.go | 26 ++++++++++++++++++++++++++ require/require_forward.go | 20 ++++++++++++++++++++ 6 files changed, 116 insertions(+) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 27e2420ed..7880b8f94 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -736,6 +736,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) +} + // YAMLEqf asserts that two YAML strings are equivalent. func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index d9ea368d0..339515b8b 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -1461,6 +1461,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta return WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/assert/assertions.go b/assert/assertions.go index 580fdea4c..1fcb980f4 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1110,6 +1110,27 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, return true } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if end.Before(start) { + return Fail(t, "Start should be before end", msgAndArgs...) + } + + if actual.Before(start) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) + } else if actual.After(end) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) + } + + return true +} + func toFloat(x interface{}) (float64, bool) { var xf float64 xok := true diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 7a35a648e..f407422d8 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1350,6 +1350,25 @@ func TestWithinDuration(t *testing.T) { False(t, WithinDuration(mockT, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") } +func TestWithinRange(t *testing.T) { + + mockT := new(testing.T) + n := time.Now() + s := n.Add(-time.Second) + e := n.Add(time.Second) + + True(t, WithinRange(mockT, n, n, n), "Exact same actual, start, and end values return true") + + True(t, WithinRange(mockT, n, s, e), "Time in range is within the time range") + True(t, WithinRange(mockT, s, s, e), "The start time is within the time range") + True(t, WithinRange(mockT, e, s, e), "The end time is within the time range") + + False(t, WithinRange(mockT, s.Add(-time.Nanosecond), s, e, "Just before the start time is not within the time range")) + False(t, WithinRange(mockT, e.Add(time.Nanosecond), s, e, "Just after the end time is not within the time range")) + + False(t, WithinRange(mockT, n, e, s, "Just after the end time is not within the time range")) +} + func TestInDelta(t *testing.T) { mockT := new(testing.T) diff --git a/require/require.go b/require/require.go index 59c48277a..880853f5a 100644 --- a/require/require.go +++ b/require/require.go @@ -1864,6 +1864,32 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim t.FailNow() } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRange(t, actual, start, end, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRangef(t, actual, start, end, msg, args...) { + return + } + t.FailNow() +} + // YAMLEq asserts that two YAML strings are equivalent. func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { diff --git a/require/require_forward.go b/require/require_forward.go index 5bb07c89c..960bf6f2c 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -1462,6 +1462,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { From 66eef0ef3adb4691cdc275620c9a129f2f98935a Mon Sep 17 00:00:00 2001 From: Oladapo Ajala Date: Tue, 28 Jun 2022 12:24:53 +0100 Subject: [PATCH 2/4] fix: assert.MapSubset (or just support maps in assert.Subset) (#1178) * WIP: added map key value check in subset * upgraded subset & notsubset to check handle maps --- assert/assertions.go | 46 ++++++++++++++++++++++++++++++++++----- assert/assertions_test.go | 16 ++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 1fcb980f4..6355dd2cf 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -816,7 +816,6 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true // we consider nil to be equal to the nil set } - subsetValue := reflect.ValueOf(subset) defer func() { if e := recover(); e != nil { ok = false @@ -826,14 +825,32 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...) + } + } + + return true + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) @@ -860,7 +877,6 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) defer func() { if e := recover(); e != nil { ok = false @@ -870,14 +886,32 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return true + } + } + + return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index f407422d8..bdab184c1 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -685,11 +685,27 @@ func TestSubsetNotSubset(t *testing.T) { {[]int{1, 2, 3}, []int{1, 2}, true, "[1, 2, 3] contains [1, 2]"}, {[]int{1, 2, 3}, []int{1, 2, 3}, true, "[1, 2, 3] contains [1, 2, 3"}, {[]string{"hello", "world"}, []string{"hello"}, true, "[\"hello\", \"world\"] contains [\"hello\"]"}, + {map[string]string{ + "a": "x", + "c": "z", + "b": "y", + }, map[string]string{ + "a": "x", + "b": "y", + }, true, `{ "a": "x", "b": "y", "c": "z"} contains { "a": "x", "b": "y"}`}, // cases that are expected not to contain {[]string{"hello", "world"}, []string{"hello", "testify"}, false, "[\"hello\", \"world\"] does not contain [\"hello\", \"testify\"]"}, {[]int{1, 2, 3}, []int{4, 5}, false, "[1, 2, 3] does not contain [4, 5"}, {[]int{1, 2, 3}, []int{1, 5}, false, "[1, 2, 3] does not contain [1, 5]"}, + {map[string]string{ + "a": "x", + "c": "z", + "b": "y", + }, map[string]string{ + "a": "x", + "b": "z", + }, false, `{ "a": "x", "b": "y", "c": "z"} does not contain { "a": "x", "b": "z"}`}, } for _, c := range cases { From cf1284f8dd6f0bf34eed4ab8808ef88f40f7d00f Mon Sep 17 00:00:00 2001 From: Bracken Date: Tue, 28 Jun 2022 13:13:45 +0100 Subject: [PATCH 3/4] Allow mock expectations to be ordered (#1106) * Allow mock expectations to be ordered * Only say another call if it has been called before --- mock/mock.go | 72 ++++++++++++++---- mock/mock_test.go | 189 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 14 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index 3833cb6f0..f0af8246c 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -70,6 +70,9 @@ type Call struct { // if the PanicMsg is set to a non nil string the function call will panic // irrespective of other settings PanicMsg *string + + // Calls which must be satisfied before this call can be + requires []*Call } func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { @@ -236,6 +239,27 @@ func (c *Call) Unset() *Call { return c } +// NotBefore indicates that the mock should only be called after the referenced +// calls have been called as expected. The referenced calls may be from the +// same mock instance and/or other mock instances. +// +// Mock.On("Do").Return(nil).Notbefore( +// Mock.On("Init").Return(nil) +// ) +func (c *Call) NotBefore(calls ...*Call) *Call { + c.lock() + defer c.unlock() + + for _, call := range calls { + if call.Parent == nil { + panic("not before calls must be created with Mock.On()") + } + } + + c.requires = append(c.requires, calls...) + return c +} + // Mock is the workhorse used to track activity on another object. // For an example of its usage, refer to the "Example Usage" section at the top // of this document. @@ -462,6 +486,25 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen } } + for _, requirement := range call.requires { + if satisfied, _ := requirement.Parent.checkExpectation(requirement); !satisfied { + m.mutex.Unlock() + m.fail("mock: Unexpected Method Call\n-----------------------------\n\n%s\n\nMust not be called before%s:\n\n%s", + callString(call.Method, call.Arguments, true), + func() (s string) { + if requirement.totalCalls > 0 { + s = " another call of" + } + if call.Parent != requirement.Parent { + s += " method from another mock instance" + } + return + }(), + callString(requirement.Method, requirement.Arguments, true), + ) + } + } + if call.Repeatability == 1 { call.Repeatability = -1 } else if call.Repeatability > 1 { @@ -541,32 +584,33 @@ func (m *Mock) AssertExpectations(t TestingT) bool { m.mutex.Lock() defer m.mutex.Unlock() - var somethingMissing bool var failedExpectations int // iterate through each expectation expectedCalls := m.expectedCalls() for _, expectedCall := range expectedCalls { - if !expectedCall.optional && !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) && expectedCall.totalCalls == 0 { - somethingMissing = true + satisfied, reason := m.checkExpectation(expectedCall) + if !satisfied { failedExpectations++ - t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo) - } else { - if expectedCall.Repeatability > 0 { - somethingMissing = true - failedExpectations++ - t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo) - } else { - t.Logf("PASS:\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String()) - } } + t.Logf(reason) } - if somethingMissing { + if failedExpectations != 0 { t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo()) } - return !somethingMissing + return failedExpectations == 0 +} + +func (m *Mock) checkExpectation(call *Call) (bool, string) { + if !call.optional && !m.methodWasCalled(call.Method, call.Arguments) && call.totalCalls == 0 { + return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) + } + if call.Repeatability > 0 { + return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) + } + return true, fmt.Sprintf("PASS:\t%s(%s)", call.Method, call.Arguments.String()) } // AssertNumberOfCalls asserts that the method was called expectedCalls times. diff --git a/mock/mock_test.go b/mock/mock_test.go index 06b25012e..5cb40e366 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -820,6 +820,195 @@ func Test_Mock_Return_Nothing(t *testing.T) { assert.Equal(t, 0, len(call.ReturnArguments)) } +func Test_Mock_Return_NotBefore_In_Order(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + b := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{b, c}, mockedService.ExpectedCalls) + require.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_Out_Of_Order(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + b := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil).Twice() + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{b, c}, mockedService.ExpectedCalls) + + expectedPanicString := `mock: Unexpected Method Call +----------------------------- + +TheExampleMethod2(bool) + 0: true + +Must not be called before: + +TheExampleMethod(int,int,int) + 0: 1 + 1: 2 + 2: 3` + require.PanicsWithValue(t, expectedPanicString, func() { + mockedService.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_Not_Enough_Times(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + b := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil).Twice() + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{b, c}, mockedService.ExpectedCalls) + + require.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + expectedPanicString := `mock: Unexpected Method Call +----------------------------- + +TheExampleMethod2(bool) + 0: true + +Must not be called before another call of: + +TheExampleMethod(int,int,int) + 0: 1 + 1: 2 + 2: 3` + require.PanicsWithValue(t, expectedPanicString, func() { + mockedService.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_Different_Mock_In_Order(t *testing.T) { + var ( + mockedService1 = new(TestExampleImplementation) + mockedService2 = new(TestExampleImplementation) + ) + + b := mockedService1. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + c := mockedService2. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{c}, mockedService2.ExpectedCalls) + require.NotPanics(t, func() { + mockedService1.TheExampleMethod(1, 2, 3) + }) + require.NotPanics(t, func() { + mockedService2.TheExampleMethod2(true) + }) +} +func Test_Mock_Return_NotBefore_Different_Mock_Out_Of_Order(t *testing.T) { + var ( + mockedService1 = new(TestExampleImplementation) + mockedService2 = new(TestExampleImplementation) + ) + + b := mockedService1. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + c := mockedService2. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{c}, mockedService2.ExpectedCalls) + + expectedPanicString := `mock: Unexpected Method Call +----------------------------- + +TheExampleMethod2(bool) + 0: true + +Must not be called before method from another mock instance: + +TheExampleMethod(int,int,int) + 0: 1 + 1: 2 + 2: 3` + require.PanicsWithValue(t, expectedPanicString, func() { + mockedService2.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_In_Order_With_Non_Dependant(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + a := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + b := mockedService. + On("TheExampleMethod", 4, 5, 6). + Return(4, nil) + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(a, b) + d := mockedService. + On("TheExampleMethod7", []bool{}).Return(nil) + + require.Equal(t, []*Call{a, b, c, d}, mockedService.ExpectedCalls) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod(4, 5, 6) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod2(true) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) +} + +func Test_Mock_Return_NotBefore_Orphan_Call(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + require.PanicsWithValue(t, "not before calls must be created with Mock.On()", func() { + mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(&Call{Method: "Not", Arguments: Arguments{"how", "it's"}, ReturnArguments: Arguments{"done"}}) + }) +} + func Test_Mock_findExpectedCall(t *testing.T) { m := new(Mock) From 181cea6eab8b2de7071383eca4be32a424db38dd Mon Sep 17 00:00:00 2001 From: Stavros Ntentos <133706+stdedos@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:56:06 +0300 Subject: [PATCH 4/4] impr: `CallerInfo` should print full paths to the terminal (#1201) * impr: `CallerInfo` should print full paths to the terminal I am proposing this simple change, which changes this output ``` --- FAIL: TestABC (0.00s) --- FAIL: TestABC/C (0.00s) /this/is/a/path/to/file_test.go:258: Error Trace: file_test.go:258 file_test.go:748 Error: Not equal: ... ``` to this: ``` --- FAIL: TestABC (0.00s) --- FAIL: TestABC/C (0.00s) /this/is/a/path/to/file_test.go:258: Error Trace: /this/is/a/path/to/file_test.go:258 /this/is/a/path/to/file_test.go:748 Error: Not equal: ... ``` With the latter output, it is much more straightforward to find the file you are looking for, even though in the displayed case, the file is the same. However, for VSCodium, the case is a little more helpful, since VSCodium's terminal is smart enough to recognize the output, and make links out of that input. Signed-off-by: Stavros Ntentos <133706+stdedos@users.noreply.github.com> * test: fix the tests that depended on the previous behavior Signed-off-by: Stavros Ntentos <133706+stdedos@users.noreply.github.com> Co-authored-by: Stavros Ntentos <11300730-stavros-relex@users.noreply.gitlab.com> --- assert/assertions.go | 4 +++- mock/mock_test.go | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 6355dd2cf..fa1245b18 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -8,6 +8,7 @@ import ( "fmt" "math" "os" + "path/filepath" "reflect" "regexp" "runtime" @@ -144,7 +145,8 @@ func CallerInfo() []string { if len(parts) > 1 { dir := parts[len(parts)-2] if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + path, _ := filepath.Abs(file) + callers = append(callers, fmt.Sprintf("%s:%d", path, line)) } } diff --git a/mock/mock_test.go b/mock/mock_test.go index 5cb40e366..f77dfe461 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -151,7 +151,7 @@ func Test_Mock_Chained_On(t *testing.T) { var mockedService = new(TestExampleImplementation) // determine our current line number so we can assert the expected calls callerInfo properly - _, _, line, _ := runtime.Caller(0) + _, filename, line, _ := runtime.Caller(0) mockedService. On("TheExampleMethod", 1, 2, 3). Return(0). @@ -164,14 +164,14 @@ func Test_Mock_Chained_On(t *testing.T) { Method: "TheExampleMethod", Arguments: []interface{}{1, 2, 3}, ReturnArguments: []interface{}{0}, - callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+2)}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+2)}, }, { Parent: &mockedService.Mock, Method: "TheExampleMethod3", Arguments: []interface{}{AnythingOfType("*mock.ExampleType")}, ReturnArguments: []interface{}{nil}, - callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+4)}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+4)}, }, } assert.Equal(t, expectedCalls, mockedService.ExpectedCalls) @@ -521,7 +521,7 @@ func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { var mockedService = new(TestExampleImplementation) // determine our current line number so we can assert the expected calls callerInfo properly - _, _, line, _ := runtime.Caller(0) + _, filename, line, _ := runtime.Caller(0) mockedService. On("TheExampleMethod1", 1, 1). Return(0). @@ -536,14 +536,14 @@ func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { Method: "TheExampleMethod1", Arguments: []interface{}{1, 1}, ReturnArguments: []interface{}{0}, - callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+2)}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+2)}, }, { Parent: &mockedService.Mock, Method: "TheExampleMethod2", Arguments: []interface{}{2, 2}, ReturnArguments: []interface{}{}, - callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+4)}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+4)}, }, } assert.Equal(t, 2, len(expectedCalls))