diff --git a/README.md b/README.md index b6fd6a3..a9f3942 100644 --- a/README.md +++ b/README.md @@ -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]: +[gomock]: +[gock]: +[monkey]: +[gin]: +[chi]: + + ## Building 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`. 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]: -[gomock]: -[gock]: -[monkey]: -[gin]: -[chi]: diff --git a/VERSION b/VERSION index c4132bc..6207741 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.51 +0.0.52 diff --git a/go.mod b/go.mod index 04e3160..4df9224 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 51827c7..a78909c 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/mock/mocks.go b/mock/mocks.go index 01f0cf4..a821724 100644 --- a/mock/mocks.go +++ b/mock/mocks.go @@ -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 @@ -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) @@ -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 } diff --git a/mock/mocks_test.go b/mock/mocks_test.go index 7979d1d..98365d1 100644 --- a/mock/mocks_test.go +++ b/mock/mocks_test.go @@ -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} })) } @@ -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() } } @@ -77,7 +77,7 @@ 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": { @@ -85,8 +85,8 @@ var mockTestCases = map[string]mockParams{ 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": { @@ -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") }, }, @@ -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": { @@ -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") }, }, @@ -258,7 +258,7 @@ var getMockTestCases = map[string]getMockParams{ test: getMockTestFunc, }, "function-constructor": { - expect: NewMockIFace, + expect: NewMockIFace[string], test: getMockTestMock, }, } @@ -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") }, @@ -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") }, @@ -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") }, @@ -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), }, } @@ -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() }) }