Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,14 @@ With a careful system design, the general pattern provided above can be used
to create parallel test for a wide range of situations.


[testing]: <https://pkg.go.dev/testing>
[gomock]: <https://go.uber.org/mock>
[gock]: <https://github.com/h2non/gock>
[monkey]: <https://github.com/bouk/monkey>
[gin]: <https://github.com/gin-gonic/gin>
[chi]: <https://github.com/go-chi/chi>


## Building

This project is using a custom build system called [go-make][go-make], that
Expand Down Expand Up @@ -273,11 +281,3 @@ project has more than 25 Stars, I will introduce semantic versions `v1`.
If you like to contribute, please create an issue and/or pull request with a
proper description of your proposal or contribution. I will review it and
provide feedback on it as fast as possible.


[testing]: <https://pkg.go.dev/testing>
[gomock]: <https://go.uber.org/mock>
[gock]: <https://github.com/h2non/gock>
[monkey]: <https://github.com/bouk/monkey>
[gin]: <https://github.com/gin-gonic/gin>
[chi]: <https://github.com/go-chi/chi>
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.51
0.0.52
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ require (
github.com/stretchr/testify v1.11.1
go.uber.org/mock v0.6.0
golang.org/x/text v0.30.0
golang.org/x/tools v0.40.0
golang.org/x/tools v0.41.0
)

require (
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
golang.org/x/mod v0.31.0 // indirect
golang.org/x/mod v0.32.0 // indirect
golang.org/x/sync v0.19.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
14 changes: 8 additions & 6 deletions mock/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type Mocks struct {
// The lenient wait group.
wg sync.WaitGroup
// The map of mock singletons.
mocks map[reflect.Value]any
mocks map[reflect.Type]any
// A map of mock key value pairs.
args map[any]any

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

// Get resolves the singleton mock from the mock handler by providing the
// reflection value of the constructor function generated by [gomock] to create
// a new mock. The mock is only created once and stored in an internal creator
// function to mock map.
// a new mock. The mock is only created once and stored in an internal type to
// mock map.
func (mocks *Mocks) Get(creator reflect.Value) any {
mock, ok := mocks.mocks[creator]
key := creator.Type()
mock, ok := mocks.mocks[key]
if ok && mock != nil {
return mock
}

mock = reflect.ArgOf(creator.Call(
reflect.ValuesIn(creator.Type(), mocks.Ctrl))[0])
mocks.mocks[creator] = mock
mocks.mocks[key] = mock
return mock
}

Expand Down
82 changes: 41 additions & 41 deletions mock/mocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ import (
"github.com/tkrop/go-testing/test"
)

type IFace interface {
CallA(input string)
CallB(input string) string
type IFace[T any] interface {
CallA(input T)
CallB(input T) T
}

func CallA(input string) mock.SetupFunc {
func CallA[T any](input T) mock.SetupFunc {
return func(mocks *mock.Mocks) any {
return mock.Get(mocks, NewMockIFace).EXPECT().
CallA(mocks.Equal(input)).Do(mocks.Do(IFace.CallA))
return mock.Get(mocks, NewMockIFace[T]).EXPECT().
CallA(mocks.Equal(input)).Do(mocks.Do(IFace[T].CallA))
}
}

func CallB(input string, output string) mock.SetupFunc {
func CallB[T any](input T, output T) mock.SetupFunc {
return func(mocks *mock.Mocks) any {
return mock.Get(mocks, NewMockIFace).EXPECT().
return mock.Get(mocks, NewMockIFace[T]).EXPECT().
CallB(mocks.Equal(input)).DoAndReturn(
mocks.Call(IFace.CallB, func(...any) []any {
mocks.Call(IFace[T].CallB, func(...any) []any {
return []any{output}
}))
}
Expand All @@ -45,9 +45,9 @@ func CallB(input string, output string) mock.SetupFunc {
// })
// }

func NoCall() mock.SetupFunc {
func NoCall[T any]() mock.SetupFunc {
return func(mocks *mock.Mocks) any {
return mock.Get(mocks, NewMockIFace).EXPECT()
return mock.Get(mocks, NewMockIFace[T]).EXPECT()
}
}

Expand Down Expand Up @@ -77,16 +77,16 @@ var mockTestCases = map[string]mockParams{
CallA("ok"),
),
call: func(_ test.Test, mocks *mock.Mocks) {
mock.Get(mocks, NewMockIFace).CallA("ok")
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
},
},
"single-mock-with-two-calls": {
setup: mock.Setup(
CallA("ok"), CallA("okay"),
),
call: func(_ test.Test, mocks *mock.Mocks) {
mock.Get(mocks, NewMockIFace).CallA("ok")
mock.Get(mocks, NewMockIFace).CallA("okay")
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
mock.Get(mocks, NewMockIFace[string]).CallA("okay")
},
},
"single-mock-with-missing-calls": {
Expand All @@ -95,26 +95,26 @@ var mockTestCases = map[string]mockParams{
),
misses: test.MissingCalls(CallA("okay")),
call: func(_ test.Test, mocks *mock.Mocks) {
mock.Get(mocks, NewMockIFace).CallA("ok")
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
},
},
"single-mock-with-unexpected-call": {
misses: test.UnexpectedCall(NewMockIFace,
misses: test.UnexpectedCall(NewMockIFace[string],
"CallA", path.Join(SourceDir, "mocks_test.go:105"), "ok"),
call: func(_ test.Test, mocks *mock.Mocks) {
mock.Get(mocks, NewMockIFace).CallA("ok")
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
},
},
"single-mock-with-more-than-expected-calls": {
setup: mock.Setup(
CallA("ok"),
),
misses: test.ConsumedCall(NewMockIFace,
misses: test.ConsumedCall(NewMockIFace[string],
"CallA", path.Join(SourceDir, "mocks_test.go:117"),
path.Join(SourceDir, "mocks_test.go:28"), "ok"),
call: func(_ test.Test, mocks *mock.Mocks) {
mock.Get(mocks, NewMockIFace).CallA("ok")
mock.Get(mocks, NewMockIFace).CallA("ok")
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
mock.Get(mocks, NewMockIFace[string]).CallA("ok")
},
},

Expand All @@ -124,8 +124,8 @@ var mockTestCases = map[string]mockParams{
CallB("okay", "okay"),
),
call: func(_ test.Test, mocks *mock.Mocks) {
mock.Get(mocks, NewMockIFace).CallA("okay")
mock.Get(mocks, NewMockIFace).CallB("okay")
mock.Get(mocks, NewMockIFace[string]).CallA("okay")
mock.Get(mocks, NewMockIFace[string]).CallB("okay")
},
},
"multiple-mocks-with-many-calls": {
Expand All @@ -135,8 +135,8 @@ var mockTestCases = map[string]mockParams{
CallC("okay"),
),
call: func(_ test.Test, mocks *mock.Mocks) {
mock.Get(mocks, NewMockIFace).CallA("okay")
mock.Get(mocks, NewMockIFace).CallB("okay")
mock.Get(mocks, NewMockIFace[string]).CallA("okay")
mock.Get(mocks, NewMockIFace[string]).CallB("okay")
mock.Get(mocks, NewMockXFace).CallC("okay")
},
},
Expand Down Expand Up @@ -258,7 +258,7 @@ var getMockTestCases = map[string]getMockParams{
test: getMockTestFunc,
},
"function-constructor": {
expect: NewMockIFace,
expect: NewMockIFace[string],
test: getMockTestMock,
},
}
Expand Down Expand Up @@ -309,7 +309,7 @@ func MockValidate(
}

func SetupPermTestABC(mocks *mock.Mocks) *perm.Test {
iface := mock.Get(mocks, NewMockIFace)
iface := mock.Get(mocks, NewMockIFace[string])
return perm.NewTest(mocks,
perm.TestMap{
"a": func(test.Test) { iface.CallA("a") },
Expand All @@ -324,7 +324,7 @@ func SetupPermTestABC(mocks *mock.Mocks) *perm.Test {
}

func SetupPermTestABCD(mocks *mock.Mocks) *perm.Test {
iface := mock.Get(mocks, NewMockIFace)
iface := mock.Get(mocks, NewMockIFace[string])
return perm.NewTest(mocks,
perm.TestMap{
"a": func(test.Test) { iface.CallA("a") },
Expand All @@ -339,7 +339,7 @@ func SetupPermTestABCD(mocks *mock.Mocks) *perm.Test {
}

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

var panicTestCases = map[string]PanicParams{
"setup": {
setup: mock.Setup(NoCall()),
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
setup: mock.Setup(NoCall[string]()),
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
},
"chain": {
setup: mock.Chain(NoCall()),
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
setup: mock.Chain(NoCall[string]()),
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
},
"parallel": {
setup: mock.Parallel(NoCall()),
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
setup: mock.Parallel(NoCall[string]()),
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
},
"detach": {
setup: mock.Detach(4, NoCall()),
setup: mock.Detach(4, NoCall[string]()),
expectError: mock.NewErrDetachMode(4),
},
"sub": {
setup: mock.Sub(0, 0, NoCall()),
expectError: mock.NewErrNoCall(NewMockIFace(nil).EXPECT()),
setup: mock.Sub(0, 0, NoCall[string]()),
expectError: mock.NewErrNoCall(NewMockIFace[string](nil).EXPECT()),
},
"sub-head": {
setup: mock.Sub(0, 0, mock.Detach(mock.Head, NoCall())),
setup: mock.Sub(0, 0, mock.Detach(mock.Head, NoCall[string]())),
expectError: mock.NewErrDetachNotAllowed(mock.Head),
},
"sub-tail": {
setup: mock.Sub(0, 0, mock.Detach(mock.Tail, NoCall())),
setup: mock.Sub(0, 0, mock.Detach(mock.Tail, NoCall[string]())),
expectError: mock.NewErrDetachNotAllowed(mock.Tail),
},
"sub-both": {
setup: mock.Sub(0, 0, mock.Detach(mock.Both, NoCall())),
setup: mock.Sub(0, 0, mock.Detach(mock.Both, NoCall[string]())),
expectError: mock.NewErrDetachNotAllowed(mock.Both),
},
}
Expand Down Expand Up @@ -978,7 +978,7 @@ func TestFailures(t *testing.T) {
param.test(t)

// Then
mock.Get(mocks, NewMockIFace).CallA("a")
mock.Get(mocks, NewMockIFace[string]).CallA("a")
mocks.Wait()
})
}
Expand Down
Loading