Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit 82f990d

Browse files
authored
Implement OverridableController to allow for mock overrides (#22)
This is an initial implementation of the proposal outlined in this issue, #685
1 parent 7e5211a commit 82f990d

File tree

5 files changed

+98
-2
lines changed

5 files changed

+98
-2
lines changed

gomock/callset.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ type callSet struct {
2929
expectedMu *sync.Mutex
3030
// Calls that have been exhausted.
3131
exhausted map[callSetKey][]*Call
32+
// when set to true, existing call expectations are overridden when new call expectations are made
33+
allowOverride bool
3234
}
3335

3436
// callSetKey is the key in the maps in callSet
@@ -45,6 +47,15 @@ func newCallSet() *callSet {
4547
}
4648
}
4749

50+
func newOverridableCallSet() *callSet {
51+
return &callSet{
52+
expected: make(map[callSetKey][]*Call),
53+
expectedMu: &sync.Mutex{},
54+
exhausted: make(map[callSetKey][]*Call),
55+
allowOverride: true,
56+
}
57+
}
58+
4859
// Add adds a new expected call.
4960
func (cs callSet) Add(call *Call) {
5061
key := callSetKey{call.receiver, call.method}
@@ -56,6 +67,10 @@ func (cs callSet) Add(call *Call) {
5667
if call.exhausted() {
5768
m = cs.exhausted
5869
}
70+
if cs.allowOverride {
71+
m[key] = make([]*Call, 0)
72+
}
73+
5974
m[key] = append(m[key], call)
6075
}
6176

gomock/callset_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@ func TestCallSetAdd(t *testing.T) {
4242
}
4343
}
4444

45+
func TestCallSetAdd_WhenOverridable_ClearsPreviousExpectedAndExhausted(t *testing.T) {
46+
method := "TestMethod"
47+
var receiver interface{} = "TestReceiver"
48+
cs := newOverridableCallSet()
49+
50+
cs.Add(newCall(t, receiver, method, reflect.TypeOf(receiverType{}.Func)))
51+
numExpectedCalls := len(cs.expected[callSetKey{receiver, method}])
52+
if numExpectedCalls != 1 {
53+
t.Fatalf("Expected 1 expected call in callset, got %d", numExpectedCalls)
54+
}
55+
56+
cs.Add(newCall(t, receiver, method, reflect.TypeOf(receiverType{}.Func)))
57+
newNumExpectedCalls := len(cs.expected[callSetKey{receiver, method}])
58+
if newNumExpectedCalls != 1 {
59+
t.Fatalf("Expected 1 expected call in callset, got %d", newNumExpectedCalls)
60+
}
61+
}
62+
4563
func TestCallSetRemove(t *testing.T) {
4664
method := "TestMethod"
4765
var receiver interface{} = "TestReceiver"

gomock/controller.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,23 @@ func NewController(t TestReporter, opts ...ControllerOption) *Controller {
108108
return ctrl
109109
}
110110

111-
// ControllerOption configures how a Controller should behave. Currently
112-
// there are no implementations of it.
111+
// ControllerOption configures how a Controller should behave.
113112
type ControllerOption interface {
114113
apply(*Controller)
115114
}
116115

116+
type overridableExpectationsOption struct{}
117+
118+
// WithOverridableExpectations allows for overridable call expectations
119+
// i.e., subsequent call expectations override existing call expectations
120+
func WithOverridableExpectations() overridableExpectationsOption {
121+
return overridableExpectationsOption{}
122+
}
123+
124+
func (o overridableExpectationsOption) apply(ctrl *Controller) {
125+
ctrl.expectedCalls = newOverridableCallSet()
126+
}
127+
117128
type cancelReporter struct {
118129
t TestHelper
119130
cancel func()

gomock/example_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,21 @@ func ExampleCall_DoAndReturn_captureArguments() {
4848
fmt.Printf("%s %s", r, s)
4949
// Output: I'm sleepy foo
5050
}
51+
52+
func ExampleCall_DoAndReturn_withOverridableExpectations() {
53+
t := &testing.T{} // provided by test
54+
ctrl := gomock.NewController(t, gomock.WithOverridableExpectations())
55+
mockIndex := NewMockFoo(ctrl)
56+
var s string
57+
58+
mockIndex.EXPECT().Bar(gomock.AssignableToTypeOf(s)).DoAndReturn(
59+
func(arg string) interface{} {
60+
s = arg
61+
return "I'm sleepy"
62+
},
63+
)
64+
65+
r := mockIndex.Bar("foo")
66+
fmt.Printf("%s %s", r, s)
67+
// Output: I'm sleepy foo
68+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package gomock_test
2+
3+
import (
4+
"testing"
5+
6+
"go.uber.org/mock/gomock"
7+
)
8+
9+
func TestEcho_NoOverride(t *testing.T) {
10+
ctrl := gomock.NewController(t, gomock.WithOverridableExpectations())
11+
mockIndex := NewMockFoo(ctrl)
12+
13+
mockIndex.EXPECT().Bar(gomock.Any()).Return("foo")
14+
res := mockIndex.Bar("input")
15+
16+
if res != "foo" {
17+
t.Fatalf("expected response to equal 'foo', got %s", res)
18+
}
19+
}
20+
21+
func TestEcho_WithOverride_BaseCase(t *testing.T) {
22+
ctrl := gomock.NewController(t, gomock.WithOverridableExpectations())
23+
mockIndex := NewMockFoo(ctrl)
24+
25+
// initial expectation set
26+
mockIndex.EXPECT().Bar(gomock.Any()).Return("foo")
27+
// override
28+
mockIndex.EXPECT().Bar(gomock.Any()).Return("bar")
29+
res := mockIndex.Bar("input")
30+
31+
if res != "bar" {
32+
t.Fatalf("expected response to equal 'bar', got %s", res)
33+
}
34+
}

0 commit comments

Comments
 (0)