Skip to content

Commit 0e9a75a

Browse files
authored
fix: mock setup for generic types (#141)
Signed-off-by: Tronje Krop <tronje.krop@jactors.de>
1 parent ee1fdb9 commit 0e9a75a

6 files changed

Lines changed: 64 additions & 62 deletions

File tree

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,14 @@ With a careful system design, the general pattern provided above can be used
233233
to create parallel test for a wide range of situations.
234234

235235

236+
[testing]: <https://pkg.go.dev/testing>
237+
[gomock]: <https://go.uber.org/mock>
238+
[gock]: <https://github.com/h2non/gock>
239+
[monkey]: <https://github.com/bouk/monkey>
240+
[gin]: <https://github.com/gin-gonic/gin>
241+
[chi]: <https://github.com/go-chi/chi>
242+
243+
236244
## Building
237245

238246
This project is using a custom build system called [go-make][go-make], that
@@ -273,11 +281,3 @@ project has more than 25 Stars, I will introduce semantic versions `v1`.
273281
If you like to contribute, please create an issue and/or pull request with a
274282
proper description of your proposal or contribution. I will review it and
275283
provide feedback on it as fast as possible.
276-
277-
278-
[testing]: <https://pkg.go.dev/testing>
279-
[gomock]: <https://go.uber.org/mock>
280-
[gock]: <https://github.com/h2non/gock>
281-
[monkey]: <https://github.com/bouk/monkey>
282-
[gin]: <https://github.com/gin-gonic/gin>
283-
[chi]: <https://github.com/go-chi/chi>

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.51
1+
0.0.52

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ require (
1010
github.com/stretchr/testify v1.11.1
1111
go.uber.org/mock v0.6.0
1212
golang.org/x/text v0.30.0
13-
golang.org/x/tools v0.40.0
13+
golang.org/x/tools v0.41.0
1414
)
1515

1616
require (
1717
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
18-
golang.org/x/mod v0.31.0 // indirect
18+
golang.org/x/mod v0.32.0 // indirect
1919
golang.org/x/sync v0.19.0 // indirect
2020
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
2121
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
2828
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
2929
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
3030
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
31-
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
32-
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
31+
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
32+
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
3333
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
3434
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
3535
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
3636
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
37-
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
38-
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
37+
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
38+
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
3939
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4040
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
4141
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

mock/mocks.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ type Mocks struct {
7979
// The lenient wait group.
8080
wg sync.WaitGroup
8181
// The map of mock singletons.
82-
mocks map[reflect.Value]any
82+
mocks map[reflect.Type]any
8383
// A map of mock key value pairs.
8484
args map[any]any
8585

@@ -93,7 +93,7 @@ func NewMocks(t gomock.TestReporter, fncalls ...ConfigFunc) *Mocks {
9393
return (&Mocks{
9494
Ctrl: gomock.NewController(t),
9595
wg: sync.NewLenientWaitGroup(),
96-
mocks: map[reflect.Value]any{},
96+
mocks: map[reflect.Type]any{},
9797
args: map[any]any{},
9898
diff: NewDiffConfig(),
9999
}).Config(fncalls...).syncWith(t)
@@ -117,16 +117,18 @@ func (mocks *Mocks) Expect(fncalls SetupFunc) *Mocks {
117117

118118
// Get resolves the singleton mock from the mock handler by providing the
119119
// reflection value of the constructor function generated by [gomock] to create
120-
// a new mock. The mock is only created once and stored in an internal creator
121-
// function to mock map.
120+
// a new mock. The mock is only created once and stored in an internal type to
121+
// mock map.
122122
func (mocks *Mocks) Get(creator reflect.Value) any {
123-
mock, ok := mocks.mocks[creator]
123+
key := creator.Type()
124+
mock, ok := mocks.mocks[key]
124125
if ok && mock != nil {
125126
return mock
126127
}
128+
127129
mock = reflect.ArgOf(creator.Call(
128130
reflect.ValuesIn(creator.Type(), mocks.Ctrl))[0])
129-
mocks.mocks[creator] = mock
131+
mocks.mocks[key] = mock
130132
return mock
131133
}
132134

mock/mocks_test.go

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,23 @@ import (
1717
"github.com/tkrop/go-testing/test"
1818
)
1919

20-
type IFace interface {
21-
CallA(input string)
22-
CallB(input string) string
20+
type IFace[T any] interface {
21+
CallA(input T)
22+
CallB(input T) T
2323
}
2424

25-
func CallA(input string) mock.SetupFunc {
25+
func CallA[T any](input T) mock.SetupFunc {
2626
return func(mocks *mock.Mocks) any {
27-
return mock.Get(mocks, NewMockIFace).EXPECT().
28-
CallA(mocks.Equal(input)).Do(mocks.Do(IFace.CallA))
27+
return mock.Get(mocks, NewMockIFace[T]).EXPECT().
28+
CallA(mocks.Equal(input)).Do(mocks.Do(IFace[T].CallA))
2929
}
3030
}
3131

32-
func CallB(input string, output string) mock.SetupFunc {
32+
func CallB[T any](input T, output T) mock.SetupFunc {
3333
return func(mocks *mock.Mocks) any {
34-
return mock.Get(mocks, NewMockIFace).EXPECT().
34+
return mock.Get(mocks, NewMockIFace[T]).EXPECT().
3535
CallB(mocks.Equal(input)).DoAndReturn(
36-
mocks.Call(IFace.CallB, func(...any) []any {
36+
mocks.Call(IFace[T].CallB, func(...any) []any {
3737
return []any{output}
3838
}))
3939
}
@@ -45,9 +45,9 @@ func CallB(input string, output string) mock.SetupFunc {
4545
// })
4646
// }
4747

48-
func NoCall() mock.SetupFunc {
48+
func NoCall[T any]() mock.SetupFunc {
4949
return func(mocks *mock.Mocks) any {
50-
return mock.Get(mocks, NewMockIFace).EXPECT()
50+
return mock.Get(mocks, NewMockIFace[T]).EXPECT()
5151
}
5252
}
5353

@@ -77,16 +77,16 @@ var mockTestCases = map[string]mockParams{
7777
CallA("ok"),
7878
),
7979
call: func(_ test.Test, mocks *mock.Mocks) {
80-
mock.Get(mocks, NewMockIFace).CallA("ok")
80+
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
8181
},
8282
},
8383
"single-mock-with-two-calls": {
8484
setup: mock.Setup(
8585
CallA("ok"), CallA("okay"),
8686
),
8787
call: func(_ test.Test, mocks *mock.Mocks) {
88-
mock.Get(mocks, NewMockIFace).CallA("ok")
89-
mock.Get(mocks, NewMockIFace).CallA("okay")
88+
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
89+
mock.Get(mocks, NewMockIFace[string]).CallA("okay")
9090
},
9191
},
9292
"single-mock-with-missing-calls": {
@@ -95,26 +95,26 @@ var mockTestCases = map[string]mockParams{
9595
),
9696
misses: test.MissingCalls(CallA("okay")),
9797
call: func(_ test.Test, mocks *mock.Mocks) {
98-
mock.Get(mocks, NewMockIFace).CallA("ok")
98+
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
9999
},
100100
},
101101
"single-mock-with-unexpected-call": {
102-
misses: test.UnexpectedCall(NewMockIFace,
102+
misses: test.UnexpectedCall(NewMockIFace[string],
103103
"CallA", path.Join(SourceDir, "mocks_test.go:105"), "ok"),
104104
call: func(_ test.Test, mocks *mock.Mocks) {
105-
mock.Get(mocks, NewMockIFace).CallA("ok")
105+
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
106106
},
107107
},
108108
"single-mock-with-more-than-expected-calls": {
109109
setup: mock.Setup(
110110
CallA("ok"),
111111
),
112-
misses: test.ConsumedCall(NewMockIFace,
112+
misses: test.ConsumedCall(NewMockIFace[string],
113113
"CallA", path.Join(SourceDir, "mocks_test.go:117"),
114114
path.Join(SourceDir, "mocks_test.go:28"), "ok"),
115115
call: func(_ test.Test, mocks *mock.Mocks) {
116-
mock.Get(mocks, NewMockIFace).CallA("ok")
117-
mock.Get(mocks, NewMockIFace).CallA("ok")
116+
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
117+
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
118118
},
119119
},
120120

@@ -124,8 +124,8 @@ var mockTestCases = map[string]mockParams{
124124
CallB("okay", "okay"),
125125
),
126126
call: func(_ test.Test, mocks *mock.Mocks) {
127-
mock.Get(mocks, NewMockIFace).CallA("okay")
128-
mock.Get(mocks, NewMockIFace).CallB("okay")
127+
mock.Get(mocks, NewMockIFace[string]).CallA("okay")
128+
mock.Get(mocks, NewMockIFace[string]).CallB("okay")
129129
},
130130
},
131131
"multiple-mocks-with-many-calls": {
@@ -135,8 +135,8 @@ var mockTestCases = map[string]mockParams{
135135
CallC("okay"),
136136
),
137137
call: func(_ test.Test, mocks *mock.Mocks) {
138-
mock.Get(mocks, NewMockIFace).CallA("okay")
139-
mock.Get(mocks, NewMockIFace).CallB("okay")
138+
mock.Get(mocks, NewMockIFace[string]).CallA("okay")
139+
mock.Get(mocks, NewMockIFace[string]).CallB("okay")
140140
mock.Get(mocks, NewMockXFace).CallC("okay")
141141
},
142142
},
@@ -258,7 +258,7 @@ var getMockTestCases = map[string]getMockParams{
258258
test: getMockTestFunc,
259259
},
260260
"function-constructor": {
261-
expect: NewMockIFace,
261+
expect: NewMockIFace[string],
262262
test: getMockTestMock,
263263
},
264264
}
@@ -309,7 +309,7 @@ func MockValidate(
309309
}
310310

311311
func SetupPermTestABC(mocks *mock.Mocks) *perm.Test {
312-
iface := mock.Get(mocks, NewMockIFace)
312+
iface := mock.Get(mocks, NewMockIFace[string])
313313
return perm.NewTest(mocks,
314314
perm.TestMap{
315315
"a": func(test.Test) { iface.CallA("a") },
@@ -324,7 +324,7 @@ func SetupPermTestABC(mocks *mock.Mocks) *perm.Test {
324324
}
325325

326326
func SetupPermTestABCD(mocks *mock.Mocks) *perm.Test {
327-
iface := mock.Get(mocks, NewMockIFace)
327+
iface := mock.Get(mocks, NewMockIFace[string])
328328
return perm.NewTest(mocks,
329329
perm.TestMap{
330330
"a": func(test.Test) { iface.CallA("a") },
@@ -339,7 +339,7 @@ func SetupPermTestABCD(mocks *mock.Mocks) *perm.Test {
339339
}
340340

341341
func SetupPermTestABCDEF(mocks *mock.Mocks) *perm.Test {
342-
iface := mock.Get(mocks, NewMockIFace)
342+
iface := mock.Get(mocks, NewMockIFace[string])
343343
return perm.NewTest(mocks,
344344
perm.TestMap{
345345
"a": func(test.Test) { iface.CallA("a") },
@@ -620,35 +620,35 @@ type PanicParams struct {
620620

621621
var panicTestCases = map[string]PanicParams{
622622
"setup": {
623-
setup: mock.Setup(NoCall()),
624-
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
623+
setup: mock.Setup(NoCall[string]()),
624+
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
625625
},
626626
"chain": {
627-
setup: mock.Chain(NoCall()),
628-
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
627+
setup: mock.Chain(NoCall[string]()),
628+
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
629629
},
630630
"parallel": {
631-
setup: mock.Parallel(NoCall()),
632-
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
631+
setup: mock.Parallel(NoCall[string]()),
632+
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
633633
},
634634
"detach": {
635-
setup: mock.Detach(4, NoCall()),
635+
setup: mock.Detach(4, NoCall[string]()),
636636
expectError: mock.NewErrDetachMode(4),
637637
},
638638
"sub": {
639-
setup: mock.Sub(0, 0, NoCall()),
640-
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
639+
setup: mock.Sub(0, 0, NoCall[string]()),
640+
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
641641
},
642642
"sub-head": {
643-
setup: mock.Sub(0, 0, mock.Detach(mock.Head, NoCall())),
643+
setup: mock.Sub(0, 0, mock.Detach(mock.Head, NoCall[string]())),
644644
expectError: mock.NewErrDetachNotAllowed(mock.Head),
645645
},
646646
"sub-tail": {
647-
setup: mock.Sub(0, 0, mock.Detach(mock.Tail, NoCall())),
647+
setup: mock.Sub(0, 0, mock.Detach(mock.Tail, NoCall[string]())),
648648
expectError: mock.NewErrDetachNotAllowed(mock.Tail),
649649
},
650650
"sub-both": {
651-
setup: mock.Sub(0, 0, mock.Detach(mock.Both, NoCall())),
651+
setup: mock.Sub(0, 0, mock.Detach(mock.Both, NoCall[string]())),
652652
expectError: mock.NewErrDetachNotAllowed(mock.Both),
653653
},
654654
}
@@ -978,7 +978,7 @@ func TestFailures(t *testing.T) {
978978
param.test(t)
979979

980980
// Then
981-
mock.Get(mocks, NewMockIFace).CallA("a")
981+
mock.Get(mocks, NewMockIFace[string]).CallA("a")
982982
mocks.Wait()
983983
})
984984
}

0 commit comments

Comments
 (0)