├── .github ├── CODEOWNERS └── workflows │ ├── test.yaml │ └── release.yaml ├── tools └── tools.go ├── caller.go ├── template.go ├── .gitignore ├── .goreleaser.yml ├── camel_to_snake_test.go ├── go.mod ├── safe_tester.go ├── LICENSE ├── context.go ├── internal ├── templates │ └── header.tmpl └── types │ └── interface.go ├── camel_to_snake.go ├── mock_controller_test.go ├── mock_controller.go ├── tests ├── types.go ├── actor_mock_test.go ├── context_accepter_mock_test.go ├── generic_out.go ├── formatter_mock_test.go ├── formatter_with_custom_name_mock_test.go ├── generic_in.go ├── reader_mock.go ├── generic_specific.go ├── generic_inout.go ├── generic_simple_union.go ├── generic_inline_union.go └── generic_complex_union.go ├── equal.go ├── Makefile ├── go.sum ├── doc.go ├── README.md └── cmd └── minimock └── minimock.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @hexdigest @auvn @regeda @superstas @zcolleen 2 | /.github/ @hexdigest @auvn @regeda @superstas 3 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | package minimock 4 | 5 | // https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md 6 | import ( 7 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint" 8 | _ "github.com/goreleaser/goreleaser" 9 | ) 10 | -------------------------------------------------------------------------------- /caller.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | func CallerInfo(skip int) string { 9 | if _, file, line, ok := runtime.Caller(skip + 1); ok { 10 | return fmt.Sprintf("%s:%d", file, line) 11 | } 12 | return "unknown file" 13 | } 14 | -------------------------------------------------------------------------------- /template.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import _ "embed" 4 | 5 | // HeaderTemplate is used to generate package clause and go:generate instruction 6 | // 7 | //go:embed internal/templates/header.tmpl 8 | var HeaderTemplate string 9 | 10 | // BodyTemplate is used to generate mock body 11 | // 12 | //go:embed internal/templates/body.tmpl 13 | var BodyTemplate string 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: test 3 | on: pull_request 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | permissions: {} 8 | steps: 9 | - uses: actions/checkout@v4.1.1 10 | - uses: actions/setup-go@v5.0.0 11 | with: 12 | go-version: '1.21.5' 13 | - run: make lint 14 | - run: make test 15 | # added to test if everything is regenerated without errors 16 | - run: make generate 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | /examples/sample_interface_mock.go 27 | /cmd/minimock/minimock 28 | /minimock 29 | /dist 30 | /bin 31 | /.gh_token 32 | 33 | .idea 34 | .vscode 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | on: 4 | push: 5 | tags: [v*] 6 | permissions: 7 | contents: write # To create releases 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4.1.1 13 | - uses: actions/setup-go@v5.0.0 14 | with: 15 | go-version: '1.21.5' 16 | - uses: goreleaser/goreleaser-action@v5.0.0 17 | with: 18 | version: v1.23.0 19 | args: release --clean 20 | env: 21 | GITHUB_TOKEN: ${{ github.token }} 22 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: minimock 2 | builds: 3 | - 4 | main: ./cmd/minimock/ 5 | binary: minimock 6 | env: 7 | - CGO_ENABLED=0 8 | - GO386=softfloat 9 | goos: 10 | - darwin 11 | - linux 12 | - windows 13 | 14 | ldflags: 15 | - -X main.version={{.Tag}} -X main.commit={{.FullCommit}} -X main.buildDate={{.Date}} 16 | 17 | env_files: 18 | github_token: .gh_token 19 | 20 | checksum: 21 | name_template: 'checksums.txt' 22 | snapshot: 23 | name_template: "{{ .Tag }}" 24 | changelog: 25 | sort: asc 26 | filters: 27 | exclude: 28 | - '^docs:' 29 | - '^test:' 30 | -------------------------------------------------------------------------------- /camel_to_snake_test.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_CamelToSnake(t *testing.T) { 10 | assert.Equal(t, "i_love_golang_and_json_so_much", CamelToSnake("ILoveGolangAndJSONSoMuch")) 11 | assert.Equal(t, "i_love_json", CamelToSnake("ILoveJSON")) 12 | assert.Equal(t, "json", CamelToSnake("json")) 13 | assert.Equal(t, "json", CamelToSnake("JSON")) 14 | assert.Equal(t, "привет_мир", CamelToSnake("ПриветМир")) 15 | } 16 | 17 | func Benchmark_CamelToSnake(b *testing.B) { 18 | for n := 0; n < b.N; n++ { 19 | CamelToSnake("TestTable") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gojuno/minimock/v3 2 | 3 | require ( 4 | github.com/davecgh/go-spew v1.1.1 5 | github.com/hexdigest/gowrap v1.4.3 6 | github.com/pkg/errors v0.9.1 7 | github.com/pmezard/go-difflib v1.0.0 8 | github.com/stretchr/testify v1.8.4 9 | golang.org/x/tools v0.36.0 10 | google.golang.org/protobuf v1.30.0 11 | ) 12 | 13 | require golang.org/x/sync v0.16.0 // indirect 14 | 15 | require ( 16 | github.com/gofrs/uuid/v5 v5.3.0 17 | github.com/kr/text v0.2.0 // indirect 18 | golang.org/x/mod v0.27.0 // indirect 19 | golang.org/x/text v0.22.0 20 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 21 | gopkg.in/yaml.v3 v3.0.1 // indirect 22 | ) 23 | 24 | go 1.23.0 25 | 26 | toolchain go1.24.6 27 | -------------------------------------------------------------------------------- /safe_tester.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import "sync" 4 | 5 | type safeTester struct { 6 | Tester 7 | m sync.Mutex 8 | } 9 | 10 | func newSafeTester(t Tester) *safeTester { 11 | return &safeTester{Tester: t} 12 | } 13 | 14 | // Error implements Tester 15 | func (st *safeTester) Error(args ...interface{}) { 16 | st.m.Lock() 17 | defer st.m.Unlock() 18 | st.Tester.Helper() 19 | 20 | st.Tester.Error(args...) 21 | } 22 | 23 | // Errorf implements Tester 24 | func (st *safeTester) Errorf(format string, args ...interface{}) { 25 | st.m.Lock() 26 | defer st.m.Unlock() 27 | st.Tester.Helper() 28 | 29 | st.Tester.Errorf(format, args...) 30 | } 31 | 32 | // Fatal implements Tester 33 | func (st *safeTester) Fatal(args ...interface{}) { 34 | st.m.Lock() 35 | defer st.m.Unlock() 36 | st.Tester.Helper() 37 | 38 | st.Tester.Fatal(args...) 39 | } 40 | 41 | // Fatalf implements Tester 42 | func (st *safeTester) Fatalf(format string, args ...interface{}) { 43 | st.m.Lock() 44 | defer st.m.Unlock() 45 | st.Tester.Helper() 46 | 47 | st.Tester.Fatalf(format, args...) 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Juno Lab Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | ) 7 | 8 | func copyValue(value interface{}) reflect.Value { 9 | rValue := reflect.ValueOf(value) 10 | newValue := reflect.New(rValue.Type()).Elem() 11 | newValue.Set(rValue) 12 | 13 | return newValue 14 | } 15 | 16 | func checkAnyContext(eFieldValue, aFieldValue interface{}) bool { 17 | _, ok := eFieldValue.(anyContext) 18 | if ok { 19 | if ctx, ok := aFieldValue.(context.Context); ok && ctx != nil { 20 | return true 21 | } 22 | } 23 | 24 | return false 25 | } 26 | 27 | func setAnyContext(src, dst interface{}) interface{} { 28 | srcp := copyValue(src) 29 | dstp := copyValue(dst) 30 | 31 | for i := 0; i < srcp.NumField(); i++ { 32 | srcFieldValue := unexportedVal(srcp.Field(i)) 33 | dstFieldValue := unexportedVal(dstp.Field(i)) 34 | 35 | if checkAnyContext(srcFieldValue.Interface(), dstFieldValue.Interface()) { 36 | // we set context field to anyContext because 37 | // we don't want to display diff between two contexts in case 38 | // of anyContext 39 | dstFieldValue.Set(srcFieldValue) 40 | } 41 | } 42 | 43 | return dstp.Interface() 44 | } 45 | -------------------------------------------------------------------------------- /internal/templates/header.tmpl: -------------------------------------------------------------------------------- 1 | {{ $packageName := $.Package.Name }} 2 | {{if $.Options.HeaderVars.PackageName }} 3 | {{ $packageName = $.Options.HeaderVars.PackageName }} 4 | {{end}} 5 | 6 | // Code generated by http://github.com/gojuno/minimock ({{$.Options.HeaderVars.Version}}). DO NOT EDIT. 7 | 8 | package {{$packageName}} 9 | 10 | {{if $.Options.HeaderVars.GenerateInstruction}} 11 | {{if $.Options.HeaderVars.GenerateGoRun}} 12 | //go:generate go run github.com/gojuno/minimock/v3/cmd/minimock -i {{$.SourcePackage.PkgPath}}.{{$.Options.InterfaceName}} -o {{$.Options.HeaderVars.OutputFile}} -n {{(title (index $.Vars "MockName"))}} -p {{ $packageName }} -gr 13 | {{else}} 14 | //go:generate minimock -i {{$.SourcePackage.PkgPath}}.{{$.Options.InterfaceName}} -o {{$.Options.HeaderVars.OutputFile}} -n {{(title (index $.Vars "MockName"))}} -p {{ $packageName }} 15 | {{end}} 16 | {{end}} 17 | 18 | import ( 19 | {{range $import := $.Options.Imports}}{{- if not (in $import "\"time\"" "\"sync/atomic\"" "\"github.com/gojuno/minimock/v3\"")}} 20 | {{$import}}{{end}}{{end}} 21 | mm_atomic "sync/atomic" 22 | mm_time "time" 23 | "github.com/gojuno/minimock/v3" 24 | ) -------------------------------------------------------------------------------- /camel_to_snake.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import ( 4 | "unicode" 5 | "unicode/utf8" 6 | ) 7 | 8 | type buffer struct { 9 | r []byte 10 | runeBytes [utf8.UTFMax]byte 11 | } 12 | 13 | func (b *buffer) write(r rune) { 14 | if r < utf8.RuneSelf { 15 | b.r = append(b.r, byte(r)) 16 | return 17 | } 18 | n := utf8.EncodeRune(b.runeBytes[0:], r) 19 | b.r = append(b.r, b.runeBytes[0:n]...) 20 | } 21 | 22 | func (b *buffer) indent() { 23 | if len(b.r) > 0 { 24 | b.r = append(b.r, '_') 25 | } 26 | } 27 | 28 | // CamelToSnake transforms strings from CamelCase to snake_case 29 | func CamelToSnake(s string) string { 30 | b := buffer{ 31 | r: make([]byte, 0, len(s)), 32 | } 33 | var m rune 34 | var w bool 35 | for _, ch := range s { 36 | if unicode.IsUpper(ch) { 37 | if m != 0 { 38 | if !w { 39 | b.indent() 40 | w = true 41 | } 42 | b.write(m) 43 | } 44 | m = unicode.ToLower(ch) 45 | } else { 46 | if m != 0 { 47 | b.indent() 48 | b.write(m) 49 | m = 0 50 | w = false 51 | } 52 | b.write(ch) 53 | } 54 | } 55 | if m != 0 { 56 | if !w { 57 | b.indent() 58 | } 59 | b.write(m) 60 | } 61 | return string(b.r) 62 | } 63 | -------------------------------------------------------------------------------- /mock_controller_test.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import ( 4 | "sync/atomic" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestNewController(t *testing.T) { 12 | c := NewController(t) 13 | assert.Equal(t, &safeTester{Tester: t}, c.Tester) 14 | } 15 | 16 | func TestController_RegisterMocker(t *testing.T) { 17 | c := &Controller{} 18 | c.RegisterMocker(nil) 19 | assert.Len(t, c.mockers, 1) 20 | } 21 | 22 | type dummyMocker struct { 23 | finishCounter int32 24 | waitCounter int32 25 | } 26 | 27 | func (dm *dummyMocker) MinimockFinish() { 28 | atomic.AddInt32(&dm.finishCounter, 1) 29 | } 30 | 31 | func (dm *dummyMocker) MinimockWait(time.Duration) { 32 | atomic.AddInt32(&dm.waitCounter, 1) 33 | } 34 | 35 | func TestController_Finish(t *testing.T) { 36 | dm := &dummyMocker{} 37 | c := &Controller{ 38 | mockers: []Mocker{dm, dm}, 39 | } 40 | 41 | c.Finish() 42 | assert.Equal(t, int32(2), atomic.LoadInt32(&dm.finishCounter)) 43 | } 44 | 45 | func TestController_Wait(t *testing.T) { 46 | dm := &dummyMocker{} 47 | c := &Controller{ 48 | mockers: []Mocker{dm, dm}, 49 | } 50 | 51 | c.Wait(0) 52 | assert.Equal(t, int32(2), atomic.LoadInt32(&dm.waitCounter)) 53 | } 54 | 55 | func TestController_WaitConcurrent(t *testing.T) { 56 | um1 := &unsafeMocker{} 57 | um2 := &unsafeMocker{} 58 | 59 | c := &Controller{ 60 | Tester: newSafeTester(&unsafeTester{}), 61 | mockers: []Mocker{um1, um2}, 62 | } 63 | 64 | um1.tester = c 65 | um2.tester = c 66 | 67 | c.Wait(0) //shouln't produce data races 68 | } 69 | 70 | type unsafeMocker struct { 71 | Mocker 72 | tester Tester 73 | } 74 | 75 | func (um *unsafeMocker) MinimockWait(time.Duration) { 76 | um.tester.Fatal() 77 | } 78 | 79 | type unsafeTester struct { 80 | Tester 81 | 82 | finished bool 83 | } 84 | 85 | func (u *unsafeTester) Fatal(...interface{}) { 86 | u.finished = true 87 | } 88 | 89 | func (u *unsafeTester) Helper() { 90 | 91 | } 92 | -------------------------------------------------------------------------------- /mock_controller.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | // Mocker describes common interface for all mocks generated by minimock 9 | type Mocker interface { 10 | MinimockFinish() 11 | MinimockWait(time.Duration) 12 | } 13 | 14 | // Tester contains subset of the testing.T methods used by the generated code 15 | type Tester interface { 16 | Fatal(args ...interface{}) 17 | Fatalf(format string, args ...interface{}) 18 | Error(...interface{}) 19 | Errorf(format string, args ...interface{}) 20 | FailNow() 21 | Cleanup(f func()) 22 | Helper() 23 | } 24 | 25 | // MockController can be passed to mocks generated by minimock 26 | type MockController interface { 27 | Tester 28 | 29 | RegisterMocker(Mocker) 30 | } 31 | 32 | // Controller implements MockController interface and has to be used in your tests: 33 | // mockController := minimock.NewController(t) 34 | // defer mockController.Finish() 35 | // stringerMock := NewStringerMock(mockController) 36 | type Controller struct { 37 | Tester 38 | sync.Mutex 39 | 40 | mockers []Mocker 41 | } 42 | 43 | // Check if Controller supports MockController interface 44 | var _ MockController = &Controller{} 45 | 46 | // NewController returns an instance of Controller 47 | func NewController(t Tester) *Controller { 48 | c := Controller{Tester: newSafeTester(t)} 49 | t.Cleanup(c.Finish) 50 | 51 | return &c 52 | } 53 | 54 | // RegisterMocker puts mocker to the list of controller mockers 55 | func (c *Controller) RegisterMocker(m Mocker) { 56 | c.Lock() 57 | c.mockers = append(c.mockers, m) 58 | c.Unlock() 59 | } 60 | 61 | // Finish calls to MinimockFinish method for all registered mockers 62 | // 63 | // Deprecated: Finish exists for historical compatibility 64 | // and should not be used. Current implementation of controller registers 65 | // Finish as Cleanup function for the testing.T instance passed to NewController. 66 | func (c *Controller) Finish() { 67 | c.Lock() 68 | for _, m := range c.mockers { 69 | m.MinimockFinish() 70 | } 71 | c.Unlock() 72 | } 73 | 74 | // Wait calls to MinimockWait method for all registered mockers 75 | func (c *Controller) Wait(d time.Duration) { 76 | wg := sync.WaitGroup{} 77 | wg.Add(len(c.mockers)) 78 | for _, m := range c.mockers { 79 | go func(m Mocker) { 80 | defer wg.Done() 81 | m.MinimockWait(d) 82 | }(m) 83 | } 84 | 85 | wg.Wait() 86 | } 87 | -------------------------------------------------------------------------------- /tests/types.go: -------------------------------------------------------------------------------- 1 | // Package tests contains tests for minimock tool and demonstrates minimock features 2 | package tests 3 | 4 | import ( 5 | "context" 6 | "io" 7 | 8 | "github.com/gofrs/uuid/v5" 9 | "google.golang.org/protobuf/proto" 10 | ) 11 | 12 | type ( 13 | // Alias for package with version 14 | gen = uuid.Gen 15 | 16 | //Formatter interface is used to test code generated by minimock 17 | Formatter interface { 18 | formatter //to check if variadic functions are supported 19 | } 20 | 21 | formatter interface { 22 | Format(string, ...interface{}) string //to check if variadic functions are supported 23 | } 24 | 25 | // these generic types provide all possible cases of type params declarations 26 | // This produces invalid Go code when used with minimock v3.0.10 and below 27 | genericInout[T any] interface { 28 | Name(T) T 29 | } 30 | genericOut[T any] interface { 31 | Name() T 32 | } 33 | 34 | genericIn[T any] interface { 35 | Name(T) 36 | } 37 | 38 | // following types reference some specific constraints on generic types 39 | // These are not generated properly with minimock v3.1.1 and below 40 | 41 | // Reference a specific type 42 | genericSpecific[T proto.Message] interface { 43 | Name(T) 44 | } 45 | 46 | // Reference a single type as a simple union 47 | simpleUnion interface { 48 | int 49 | } 50 | 51 | // Reference a composite union of multiple types 52 | complexUnion interface { 53 | int | float64 54 | } 55 | 56 | genericSimpleUnion[T simpleUnion] interface { 57 | Name(T) 58 | } 59 | 60 | genericComplexUnion[T complexUnion] interface { 61 | Name(T) 62 | } 63 | 64 | genericInlineUnion[T int | float64] interface { 65 | Name(T) 66 | } 67 | 68 | genericInlineUnionWithManyTypes[T int | float64 | string] interface { 69 | Name(T) 70 | } 71 | 72 | genericMultipleTypes[T proto.Message, K any] interface { 73 | Name(T, K) 74 | } 75 | 76 | formatterAlias = Formatter 77 | 78 | formatterType Formatter 79 | 80 | reader = io.Reader 81 | 82 | structArg struct { 83 | a int 84 | b string 85 | } 86 | 87 | contextAccepter interface { 88 | AcceptContext(context.Context) 89 | AcceptContextWithOtherArgs(context.Context, int) (int, error) 90 | AcceptContextWithStructArgs(context.Context, structArg) (int, error) 91 | } 92 | 93 | actor interface { 94 | Action(firstParam string, secondParam int) (int, error) 95 | } 96 | ) 97 | -------------------------------------------------------------------------------- /equal.go: -------------------------------------------------------------------------------- 1 | package minimock 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "unsafe" 7 | 8 | "github.com/davecgh/go-spew/spew" 9 | "github.com/pmezard/go-difflib/difflib" 10 | ) 11 | 12 | var dumpConf = spew.ConfigState{ 13 | Indent: " ", 14 | DisablePointerAddresses: true, 15 | SortKeys: true, 16 | } 17 | 18 | type anyContext struct { 19 | context.Context 20 | } 21 | 22 | var AnyContext = anyContext{} 23 | 24 | // Equal returns true if a equals b 25 | func Equal(a, b interface{}) bool { 26 | if a == nil && b == nil { 27 | return a == b 28 | } 29 | 30 | if reflect.TypeOf(a).Kind() == reflect.Struct { 31 | ap := copyValue(a) 32 | bp := copyValue(b) 33 | 34 | // for every field in a 35 | for i := 0; i < reflect.TypeOf(a).NumField(); i++ { 36 | aFieldValue := unexported(ap.Field(i)) 37 | bFieldValue := unexported(bp.Field(i)) 38 | 39 | if checkAnyContext(aFieldValue, bFieldValue) { 40 | continue 41 | } 42 | 43 | if !reflect.DeepEqual(aFieldValue, bFieldValue) { 44 | return false 45 | } 46 | } 47 | 48 | return true 49 | } 50 | 51 | return reflect.DeepEqual(a, b) 52 | } 53 | 54 | // Diff returns unified diff of the textual representations of e and a 55 | func Diff(e, a interface{}) string { 56 | if e == nil || a == nil { 57 | return "" 58 | } 59 | 60 | t := reflect.TypeOf(e) 61 | k := t.Kind() 62 | 63 | if reflect.TypeOf(a) != t { 64 | return "" 65 | } 66 | 67 | initialKind := k 68 | if k == reflect.Ptr { 69 | t = t.Elem() 70 | k = t.Kind() 71 | } 72 | 73 | if k != reflect.Array && k != reflect.Map && k != reflect.Slice && k != reflect.Struct { 74 | return "" 75 | } 76 | 77 | if initialKind == reflect.Struct { 78 | a = setAnyContext(e, a) 79 | } 80 | 81 | es := dumpConf.Sdump(e) 82 | as := dumpConf.Sdump(a) 83 | 84 | diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ 85 | A: difflib.SplitLines(es), 86 | B: difflib.SplitLines(as), 87 | Context: 1, 88 | FromFile: "Expected params", 89 | ToFile: "Actual params", 90 | }) 91 | 92 | if err != nil { 93 | panic(err) 94 | } 95 | 96 | return "\n\nDiff:\n" + diff 97 | } 98 | 99 | func unexported(field reflect.Value) interface{} { 100 | return unexportedVal(field).Interface() 101 | } 102 | 103 | func unexportedVal(field reflect.Value) reflect.Value { 104 | return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem() 105 | } 106 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export GOBIN := $(PWD)/bin 2 | export PATH := $(GOBIN):$(PATH) 3 | export GOFLAGS := -mod=mod 4 | 5 | all: install test lint 6 | 7 | generate: 8 | go run ./cmd/minimock/minimock.go -i github.com/gojuno/minimock/v3.Tester -o ./tests 9 | go run ./cmd/minimock/minimock.go -i ./tests.Formatter -o ./tests/formatter_mock.go 10 | go run ./cmd/minimock/minimock.go -i ./tests.Formatter -o ./tests/formatter_with_custom_name_mock.go -n CustomFormatterNameMock 11 | go run ./cmd/minimock/minimock.go -i ./tests.genericInout -o ./tests/generic_inout.go 12 | go run ./cmd/minimock/minimock.go -i ./tests.genericOut -o ./tests/generic_out.go 13 | go run ./cmd/minimock/minimock.go -i ./tests.genericIn -o ./tests/generic_in.go 14 | go run ./cmd/minimock/minimock.go -i ./tests.genericSpecific -o ./tests/generic_specific.go 15 | go run ./cmd/minimock/minimock.go -i ./tests.genericSimpleUnion -o ./tests/generic_simple_union.go 16 | go run ./cmd/minimock/minimock.go -i ./tests.genericComplexUnion -o ./tests/generic_complex_union.go 17 | go run ./cmd/minimock/minimock.go -i ./tests.genericInlineUnion -o ./tests/generic_inline_union.go 18 | go run ./cmd/minimock/minimock.go -i ./tests.genericInlineUnionWithManyTypes -o ./tests/generic_inline_with_many_options.go 19 | go run ./cmd/minimock/minimock.go -i ./tests.genericMultipleTypes -o ./tests/generic_multiple_args_with_different_types.go 20 | go run ./cmd/minimock/minimock.go -i ./tests.contextAccepter -o ./tests/context_accepter_mock.go 21 | go run ./cmd/minimock/minimock.go -i github.com/gojuno/minimock/v3.Tester -o ./tests/package_name_specified_test.go -p tests_test 22 | go run ./cmd/minimock/minimock.go -i ./tests.actor -o ./tests/actor_mock.go 23 | go run ./cmd/minimock/minimock.go -i ./tests.formatterAlias -o ./tests/formatter_alias_mock.go 24 | go run ./cmd/minimock/minimock.go -i ./tests.formatterType -o ./tests/formatter_type_mock.go 25 | go run ./cmd/minimock/minimock.go -i ./tests.reader -o ./tests/reader_mock.go -gr 26 | 27 | ./bin: 28 | mkdir ./bin 29 | 30 | lint: ./bin/golangci-lint 31 | ./bin/golangci-lint run --enable=goimports --disable=unused --exclude=S1023,"Error return value" ./tests/... 32 | 33 | install: 34 | go mod download 35 | go install ./cmd/minimock 36 | 37 | ./bin/golangci-lint: ./bin 38 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./bin v1.64.8 39 | 40 | ./bin/goreleaser: ./bin 41 | go install -modfile tools/go.mod github.com/goreleaser/goreleaser 42 | 43 | ./bin/minimock: 44 | go build ./cmd/minimock -o ./bin/minimock 45 | 46 | .PHONY: 47 | test: 48 | go test -race ./... -v 49 | 50 | build: ./bin/goreleaser 51 | ./bin/goreleaser build --snapshot --rm-dist 52 | 53 | .PHONY: 54 | tidy: 55 | go mod tidy 56 | cd tools && go mod tidy 57 | -------------------------------------------------------------------------------- /tests/actor_mock_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestActorMock_TestPassedWithBothExpectedParams(t *testing.T) { 10 | tester := NewTesterMock(t) 11 | tester.CleanupMock.Return().HelperMock.Return() 12 | 13 | mock := NewActorMock(tester). 14 | ActionMock.ExpectFirstParamParam1("abc"). 15 | ExpectSecondParamParam2(24).Return(1, nil) 16 | 17 | a, err := mock.Action("abc", 24) 18 | assert.NoError(t, err) 19 | assert.Equal(t, 1, a) 20 | } 21 | 22 | func TestActorMock_TestPassedWithOneExpectedParams(t *testing.T) { 23 | tester := NewTesterMock(t) 24 | tester.CleanupMock.Return().HelperMock.Return() 25 | 26 | mock := NewActorMock(tester). 27 | ActionMock.ExpectFirstParamParam1("abc").Return(1, nil) 28 | 29 | a, err := mock.Action("abc", 24) 30 | assert.NoError(t, err) 31 | assert.Equal(t, 1, a) 32 | } 33 | 34 | func TestActorMock_TestFailedWithExpectedParams(t *testing.T) { 35 | tester := NewTesterMock(t) 36 | tester.CleanupMock.Return().HelperMock.Return() 37 | tester.ErrorfMock.Set(func(format string, args ...interface{}) { 38 | assert.Equal(t, "ActorMock.Action got unexpected parameter secondParam, expected at\n%s:\nwant: %#v\n got: %#v%s\n", format) 39 | 40 | assert.Equal(t, 24, args[1]) 41 | assert.Equal(t, 25, args[2]) 42 | assert.Equal(t, "", args[3]) 43 | }) 44 | mock := NewActorMock(tester). 45 | ActionMock.ExpectFirstParamParam1("abc"). 46 | ExpectSecondParamParam2(24).Return(1, nil) 47 | 48 | a, err := mock.Action("abc", 25) 49 | assert.NoError(t, err) 50 | assert.Equal(t, 1, a) 51 | } 52 | 53 | func TestActorMock_FailedToUseExpectAfterExpectParams(t *testing.T) { 54 | tester := NewTesterMock(t) 55 | tester.CleanupMock.Return() 56 | tester.FatalfMock. 57 | Expect("ActorMock.Action mock is already set by ExpectParams functions"). 58 | Return() 59 | 60 | _ = NewActorMock(tester). 61 | ActionMock.ExpectFirstParamParam1("abc"). 62 | Expect("aaa", 123).Return(1, nil) 63 | } 64 | 65 | func TestActorMock_FailedToUseExpectParamsAfterExpect(t *testing.T) { 66 | tester := NewTesterMock(t) 67 | tester.CleanupMock.Return() 68 | tester.FatalfMock. 69 | Expect("ActorMock.Action mock is already set by Expect"). 70 | Return() 71 | 72 | _ = NewActorMock(tester). 73 | ActionMock.Expect("aaa", 123). 74 | ExpectFirstParamParam1("abc").Return(1, nil) 75 | } 76 | 77 | func TestActorMock_FailedToUseExpectParamsAfterSet(t *testing.T) { 78 | tester := NewTesterMock(t) 79 | tester.CleanupMock.Return() 80 | tester.FatalfMock. 81 | Expect("ActorMock.Action mock is already set by Set"). 82 | Return() 83 | 84 | _ = NewActorMock(tester). 85 | ActionMock.Set(func(firstParam string, secondParam int) (i1 int, err error) { 86 | return 87 | }).ActionMock.ExpectFirstParamParam1("abc").Return(1, nil) 88 | } 89 | 90 | func TestActorMock_Optional(t *testing.T) { 91 | tester := NewTesterMock(t) 92 | tester.CleanupMock.Return() 93 | 94 | mock := NewActorMock(tester).ActionMock.Optional().Return(1, nil) 95 | 96 | mock.MinimockFinish() 97 | } 98 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= 5 | github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= 6 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 7 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 8 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 9 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 10 | github.com/hexdigest/gowrap v1.4.3 h1:m+t8aj1pUiFQbEiE8QJg2xdYVH5DAMluLgZ9P/qEF0k= 11 | github.com/hexdigest/gowrap v1.4.3/go.mod h1:XWL8oQW2H3fX5ll8oT3Fduh4mt2H3cUAGQHQLMUbmG4= 12 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 13 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 14 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 15 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 16 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 17 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 18 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 19 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 20 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= 24 | github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 25 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 26 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 27 | golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= 28 | golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= 29 | golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= 30 | golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 31 | golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= 32 | golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= 33 | golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= 34 | golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= 35 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 36 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 37 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 38 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 39 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 40 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 41 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 42 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 43 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 44 | -------------------------------------------------------------------------------- /tests/context_accepter_mock_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/gojuno/minimock/v3" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestContextAccepterMock_AnyContext(t *testing.T) { 12 | tester := NewTesterMock(t) 13 | 14 | var mockCalled bool 15 | tester.ErrorfMock.Set(func(s string, args ...interface{}) { 16 | assert.Equal(t, "ContextAccepterMock.AcceptContext got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", s) 17 | 18 | mockCalled = true 19 | }).CleanupMock.Return().HelperMock.Return() 20 | 21 | mock := NewContextAccepterMock(tester). 22 | AcceptContextMock.Expect(context.Background()).Return() 23 | 24 | mock.AcceptContext(context.TODO()) 25 | 26 | assert.True(t, mockCalled) 27 | } 28 | 29 | func TestContextAccepterMock_TodoContextMatchesAnycontext(t *testing.T) { 30 | tester := NewTesterMock(t) 31 | tester.CleanupMock.Return().HelperMock.Return() 32 | 33 | mock := NewContextAccepterMock(tester). 34 | AcceptContextMock.Expect(minimock.AnyContext).Return() 35 | 36 | mock.AcceptContext(context.TODO()) 37 | } 38 | 39 | func TestContextAccepterMock_WhenThenMatchAnycontext(t *testing.T) { 40 | tester := NewTesterMock(t) 41 | tester.CleanupMock.Return().HelperMock.Return() 42 | 43 | mock := NewContextAccepterMock(tester). 44 | AcceptContextWithOtherArgsMock.When(minimock.AnyContext, 1).Then(42, nil) 45 | 46 | result, err := mock.AcceptContextWithOtherArgs(context.TODO(), 1) 47 | assert.NoError(t, err) 48 | assert.Equal(t, 42, result) 49 | } 50 | 51 | func TestContextAccepterMock_WhenThenMatchAnycontextWithoutArgs(t *testing.T) { 52 | tester := NewTesterMock(t) 53 | tester.CleanupMock.Return().HelperMock.Return() 54 | 55 | mock := NewContextAccepterMock(tester). 56 | AcceptContextMock.When(minimock.AnyContext).Then() 57 | 58 | mock.AcceptContext(context.TODO()) 59 | } 60 | 61 | func TestContextAccepterMock_DiffWithoutAnyContext(t *testing.T) { 62 | tester := NewTesterMock(t) 63 | tester.CleanupMock.Return().HelperMock.Return() 64 | 65 | tester.ErrorfMock.Set(func(format string, args ...interface{}) { 66 | assert.Equal(t, "ContextAccepterMock.AcceptContextWithOtherArgs got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", format) 67 | 68 | assert.Equal(t, ContextAccepterMockAcceptContextWithOtherArgsParams{ 69 | ctx: minimock.AnyContext, 70 | i1: 24, 71 | }, args[1]) 72 | assert.Equal(t, ContextAccepterMockAcceptContextWithOtherArgsParams{ 73 | ctx: context.Background(), 74 | i1: 123, 75 | }, args[2]) 76 | 77 | assert.Equal(t, "\n\nDiff:\n--- Expected params\n+++ Actual params\n@@ -4,3 +4,3 @@\n },\n- i1: (int) 24\n+ i1: (int) 123\n }\n", args[3]) 78 | }) 79 | 80 | mock := NewContextAccepterMock(tester). 81 | AcceptContextWithOtherArgsMock.Expect(minimock.AnyContext, 24).Return(1, nil) 82 | 83 | _, _ = mock.AcceptContextWithOtherArgs(context.Background(), 123) 84 | } 85 | 86 | func TestContextAccepterMock_DiffInStructArgWithoutAnyContext(t *testing.T) { 87 | tester := NewTesterMock(t) 88 | tester.CleanupMock.Return().HelperMock.Return() 89 | 90 | tester.ErrorfMock.Set(func(format string, args ...interface{}) { 91 | assert.Equal(t, "ContextAccepterMock.AcceptContextWithStructArgs got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", format) 92 | 93 | assert.Equal(t, ContextAccepterMockAcceptContextWithStructArgsParams{ 94 | ctx: minimock.AnyContext, 95 | s1: structArg{ 96 | a: 124, 97 | b: "abcd", 98 | }, 99 | }, args[1]) 100 | 101 | assert.Equal(t, ContextAccepterMockAcceptContextWithStructArgsParams{ 102 | ctx: context.Background(), 103 | s1: structArg{ 104 | a: 123, 105 | b: "abcd", 106 | }, 107 | }, args[2]) 108 | 109 | assert.Equal(t, "\n\nDiff:\n--- Expected params\n+++ Actual params\n@@ -5,3 +5,3 @@\n s1: (tests.structArg) {\n- a: (int) 124,\n+ a: (int) 123,\n b: (string) (len=4) \"abcd\"\n", args[3]) 110 | }) 111 | 112 | mock := NewContextAccepterMock(tester). 113 | AcceptContextWithStructArgsMock.Expect(minimock.AnyContext, structArg{ 114 | a: 124, 115 | b: "abcd", 116 | }). 117 | Return(1, nil) 118 | 119 | _, _ = mock.AcceptContextWithStructArgs(context.Background(), structArg{ 120 | a: 123, 121 | b: "abcd", 122 | }) 123 | } 124 | 125 | func TestContextAccepterMock_TimesSuccess(t *testing.T) { 126 | tester := NewTesterMock(t) 127 | tester.CleanupMock.Return().HelperMock.Return() 128 | 129 | mock := NewContextAccepterMock(tester). 130 | AcceptContextWithStructArgsMock.Times(2).Expect(minimock.AnyContext, structArg{ 131 | a: 124, 132 | b: "abcd", 133 | }). 134 | Return(1, nil). 135 | AcceptContextMock.Return() 136 | 137 | _, _ = mock.AcceptContextWithStructArgs(context.Background(), structArg{ 138 | a: 124, 139 | b: "abcd", 140 | }) 141 | _, _ = mock.AcceptContextWithStructArgs(context.Background(), structArg{ 142 | a: 124, 143 | b: "abcd", 144 | }) 145 | 146 | mock.AcceptContext(context.TODO()) 147 | 148 | // explicitly call MinimockFinish here to imitate call of t.Cleanup(m.MinimockFinish) 149 | // as we mocked Cleanup call 150 | mock.MinimockFinish() 151 | } 152 | 153 | func TestContextAccepterMock_TimesFailure(t *testing.T) { 154 | tester := NewTesterMock(t) 155 | tester.CleanupMock.Return().HelperMock.Return(). 156 | ErrorfMock.Set(func(format string, args ...interface{}) { 157 | assert.Equal(t, "Expected %d calls to ContextAccepterMock.AcceptContextWithStructArgs at\n%s but found %d calls", format) 158 | assert.Equal(t, uint64(1), args[0]) 159 | assert.Equal(t, uint64(2), args[2]) 160 | }) 161 | 162 | // Expected 1 calls to ContextAccepterMock.AcceptContextWithStructArgs but found 2 calls 163 | mock := NewContextAccepterMock(tester). 164 | AcceptContextWithStructArgsMock.Times(1). 165 | Expect(minimock.AnyContext, structArg{ 166 | a: 124, 167 | b: "abcd", 168 | }). 169 | Return(1, nil). 170 | AcceptContextMock. 171 | Times(1).Return() 172 | 173 | _, _ = mock.AcceptContextWithStructArgs(context.Background(), structArg{ 174 | a: 124, 175 | b: "abcd", 176 | }) 177 | _, _ = mock.AcceptContextWithStructArgs(context.Background(), structArg{ 178 | a: 124, 179 | b: "abcd", 180 | }) 181 | 182 | mock.AcceptContext(context.TODO()) 183 | 184 | // explicitly call MinimockFinish here to imitate call of t.Cleanup(m.MinimockFinish) 185 | // as we mocked Cleanup call 186 | mock.MinimockFinish() 187 | } 188 | 189 | func TestContextAccepterMock_TimesZero(t *testing.T) { 190 | tester := NewTesterMock(t) 191 | tester.CleanupMock.Return(). 192 | FatalfMock.Expect("Times of ContextAccepterMock.AcceptContextWithStructArgs mock can not be zero"). 193 | Return() 194 | 195 | _ = NewContextAccepterMock(tester). 196 | AcceptContextWithStructArgsMock.Times(0). 197 | Return(1, nil) 198 | } 199 | 200 | func TestContextAccepterMock_ExpectedCall(t *testing.T) { 201 | tester := NewTesterMock(t) 202 | tester.CleanupMock.Times(1).Return(). 203 | ErrorfMock.ExpectFormatParam1("Expected call to ContextAccepterMock.AcceptContext at\n%s").Times(1). 204 | Return() 205 | 206 | mock := NewContextAccepterMock(tester).AcceptContextMock.Return() 207 | 208 | // explicitly call MinimockFinish here to imitate call of t.Cleanup(m.MinimockFinish) 209 | // as we mocked Cleanup call 210 | mock.MinimockFinish() 211 | } 212 | -------------------------------------------------------------------------------- /internal/types/interface.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "go/ast" 6 | "go/printer" 7 | "go/token" 8 | "strings" 9 | 10 | "github.com/hexdigest/gowrap/pkg" 11 | ) 12 | 13 | // InterfaceSpecification represents abstraction over interface type. It contains all the metadata 14 | // required to render a mock for given interface. One could deduce whether interface is generic 15 | // by looking for type params 16 | type InterfaceSpecification struct { 17 | InterfaceName string 18 | InterfaceParams []InterfaceSpecificationParam 19 | } 20 | 21 | // InterfaceSpecificationParam represents a group of type param variables and their type 22 | // I.e. [T,K any] would result in names "T","K" and type "any" 23 | type InterfaceSpecificationParam struct { 24 | ParamNames []string 25 | ParamType string 26 | } 27 | 28 | func FindAllInterfaces(p *pkg.Package, pattern string) []InterfaceSpecification { 29 | // Filter interfaces from all the declarations 30 | interfaces := []*ast.TypeSpec{} 31 | for _, file := range p.Files { 32 | for _, typeSpec := range findAllTypeSpecsInFile(file) { 33 | if isInterface(typeSpec, file.Imports) { 34 | interfaces = append(interfaces, typeSpec) 35 | } 36 | } 37 | } 38 | 39 | // Filter interfaces with the given pattern 40 | filteredInterfaces := []*ast.TypeSpec{} 41 | for _, iface := range interfaces { 42 | if match(iface.Name.Name, pattern) { 43 | filteredInterfaces = append(filteredInterfaces, iface) 44 | } 45 | } 46 | 47 | // Transform AST nodes into specifications 48 | interfaceSpecifications := make([]InterfaceSpecification, 0, len(filteredInterfaces)) 49 | for _, iface := range filteredInterfaces { 50 | interfaceSpecifications = append(interfaceSpecifications, InterfaceSpecification{ 51 | InterfaceName: iface.Name.Name, 52 | InterfaceParams: getTypeParams(iface), 53 | }) 54 | } 55 | 56 | return interfaceSpecifications 57 | } 58 | 59 | func isInterface(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { 60 | // we are generating mocks for interfaces, 61 | // interface aliases to types from the same package 62 | // and aliases to types from another package 63 | return isInterfaceType(typeSpec) || 64 | isInterfaceAlias(typeSpec, fileImports) || 65 | isExportedInterfaceAlias(typeSpec, fileImports) 66 | } 67 | 68 | // isInterfaceAlias checks if type is an alias to other 69 | // interface type that is in the same package 70 | func isInterfaceAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { 71 | ident, ok := typeSpec.Type.(*ast.Ident) 72 | if !ok { 73 | return false 74 | } 75 | 76 | if ident.Obj == nil { 77 | return false 78 | } 79 | 80 | if ts, ok := ident.Obj.Decl.(*ast.TypeSpec); ok { 81 | return isInterface(ts, fileImports) 82 | } 83 | 84 | return false 85 | } 86 | 87 | // isExportedInterfaceAlias checks if type is an alias to other 88 | // interface type that is in other exported package 89 | func isExportedInterfaceAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { 90 | selector, ok := typeSpec.Type.(*ast.SelectorExpr) 91 | if !ok { 92 | return false 93 | } 94 | 95 | ident, ok := selector.X.(*ast.Ident) 96 | if !ok { 97 | return false 98 | } 99 | 100 | name := selector.Sel.Name 101 | srcPkgPath := findSourcePackage(ident, fileImports) 102 | srcPackageAst, err := getPackage(srcPkgPath) 103 | if err != nil { 104 | return false 105 | } 106 | 107 | for _, f := range srcPackageAst.Files { 108 | if f == nil { 109 | continue 110 | } 111 | types := findAllTypeSpecsInFile(f) 112 | typeSpec, found := findTypeByName(types, name) 113 | if found { 114 | // we have to check recursively because checked typed might be 115 | // another alias to other interface 116 | return isInterface(typeSpec, f.Imports) 117 | } 118 | } 119 | 120 | return false 121 | } 122 | 123 | func getPackage(packagePath string) (*pkg.Package, error) { 124 | srcPkg, err := pkg.Load(packagePath) 125 | if err != nil { 126 | return nil, err 127 | } 128 | 129 | fs := token.NewFileSet() 130 | return pkg.AST(fs, srcPkg) 131 | } 132 | 133 | func findTypeByName(types []*ast.TypeSpec, name string) (*ast.TypeSpec, bool) { 134 | for _, ts := range types { 135 | if ts.Name.Name == name { 136 | return ts, true 137 | } 138 | } 139 | 140 | return nil, false 141 | } 142 | 143 | func findSourcePackage(ident *ast.Ident, imports []*ast.ImportSpec) string { 144 | for _, imp := range imports { 145 | cleanPath := strings.Trim(imp.Path.Value, "\"") 146 | if imp.Name != nil { 147 | if ident.Name == imp.Name.Name { 148 | return cleanPath 149 | } 150 | 151 | continue 152 | } 153 | 154 | // try last segment, like in "github.com/my/package/identName" 155 | lastSlash := strings.LastIndex(cleanPath, "/") 156 | if ident.Name == cleanPath[lastSlash+1:] { 157 | return cleanPath 158 | } 159 | 160 | // try prev segment, like in "github.com/my/package/identName/v5" 161 | if cleanPath[lastSlash+1] == 'v' { 162 | prevSlash := strings.LastIndex(cleanPath[:lastSlash], "/") 163 | if ident.Name == cleanPath[prevSlash+1:lastSlash] { 164 | return cleanPath 165 | } 166 | } 167 | } 168 | 169 | // todo: should not reach here? 170 | return "" 171 | } 172 | 173 | func isInterfaceType(typeSpec *ast.TypeSpec) bool { 174 | _, ok := typeSpec.Type.(*ast.InterfaceType) 175 | return ok 176 | } 177 | 178 | // findAllInterfaceNodesInFile ranges over file's AST nodes and extracts all interfaces inside 179 | // returned *ast.TypeSpecs can be safely interpreted as interface declaration nodes 180 | func findAllTypeSpecsInFile(f *ast.File) []*ast.TypeSpec { 181 | typeSpecs := []*ast.TypeSpec{} 182 | 183 | // Range over all declarations in a single file 184 | for _, declaration := range f.Decls { 185 | // Check if declaration is an import, constant, type or variable declaration. 186 | // If it is, check specifically if it's a TYPE as all interfaces are types 187 | if genericDeclaration, ok := declaration.(*ast.GenDecl); ok && genericDeclaration.Tok == token.TYPE { 188 | // Range over all specifications and find ones that are Type declarations 189 | // This is mostly a precaution 190 | for _, spec := range genericDeclaration.Specs { 191 | // Check directly for a type spec declaration 192 | if typeSpec, ok := spec.(*ast.TypeSpec); ok { 193 | typeSpecs = append(typeSpecs, typeSpec) 194 | } 195 | } 196 | } 197 | } 198 | 199 | return typeSpecs 200 | } 201 | 202 | // match returns true if pattern is a wildcard or directly matches the given name 203 | func match(name, pattern string) bool { 204 | return pattern == "*" || name == pattern 205 | } 206 | 207 | func getTypeParams(typeSpec *ast.TypeSpec) []InterfaceSpecificationParam { 208 | params := []InterfaceSpecificationParam{} 209 | 210 | // Check whether node has any type params at all 211 | if typeSpec == nil || typeSpec.TypeParams == nil { 212 | return nil 213 | } 214 | 215 | // If node has any type params - store them in slice and return as a spec 216 | for _, param := range typeSpec.TypeParams.List { 217 | names := []string{} 218 | for _, name := range param.Names { 219 | names = append(names, name.Name) 220 | } 221 | 222 | var out bytes.Buffer 223 | printer.Fprint(&out, token.NewFileSet(), param.Type) 224 | 225 | paramType := out.String() 226 | 227 | params = append(params, InterfaceSpecificationParam{ 228 | ParamNames: names, 229 | ParamType: paramType, 230 | }) 231 | } 232 | 233 | return params 234 | } 235 | -------------------------------------------------------------------------------- /tests/generic_out.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate minimock -i github.com/gojuno/minimock/v3/tests.genericOut -o generic_out.go -n GenericOutMock -p tests 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | ) 14 | 15 | // GenericOutMock implements genericOut 16 | type GenericOutMock[T any] struct { 17 | t minimock.Tester 18 | finishOnce sync.Once 19 | 20 | funcName func() (t1 T) 21 | funcNameOrigin string 22 | inspectFuncName func() 23 | afterNameCounter uint64 24 | beforeNameCounter uint64 25 | NameMock mGenericOutMockName[T] 26 | } 27 | 28 | // NewGenericOutMock returns a mock for genericOut 29 | func NewGenericOutMock[T any](t minimock.Tester) *GenericOutMock[T] { 30 | m := &GenericOutMock[T]{t: t} 31 | 32 | if controller, ok := t.(minimock.MockController); ok { 33 | controller.RegisterMocker(m) 34 | } 35 | 36 | m.NameMock = mGenericOutMockName[T]{mock: m} 37 | 38 | t.Cleanup(m.MinimockFinish) 39 | 40 | return m 41 | } 42 | 43 | type mGenericOutMockName[T any] struct { 44 | optional bool 45 | mock *GenericOutMock[T] 46 | defaultExpectation *GenericOutMockNameExpectation[T] 47 | expectations []*GenericOutMockNameExpectation[T] 48 | 49 | expectedInvocations uint64 50 | expectedInvocationsOrigin string 51 | } 52 | 53 | // GenericOutMockNameExpectation specifies expectation struct of the genericOut.Name 54 | type GenericOutMockNameExpectation[T any] struct { 55 | mock *GenericOutMock[T] 56 | 57 | results *GenericOutMockNameResults[T] 58 | returnOrigin string 59 | Counter uint64 60 | } 61 | 62 | // GenericOutMockNameResults contains results of the genericOut.Name 63 | type GenericOutMockNameResults[T any] struct { 64 | t1 T 65 | } 66 | 67 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 68 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 69 | // Optional() makes method check to work in '0 or more' mode. 70 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 71 | // catch the problems when the expected method call is totally skipped during test run. 72 | func (mmName *mGenericOutMockName[T]) Optional() *mGenericOutMockName[T] { 73 | mmName.optional = true 74 | return mmName 75 | } 76 | 77 | // Expect sets up expected params for genericOut.Name 78 | func (mmName *mGenericOutMockName[T]) Expect() *mGenericOutMockName[T] { 79 | if mmName.mock.funcName != nil { 80 | mmName.mock.t.Fatalf("GenericOutMock.Name mock is already set by Set") 81 | } 82 | 83 | if mmName.defaultExpectation == nil { 84 | mmName.defaultExpectation = &GenericOutMockNameExpectation[T]{} 85 | } 86 | 87 | return mmName 88 | } 89 | 90 | // Inspect accepts an inspector function that has same arguments as the genericOut.Name 91 | func (mmName *mGenericOutMockName[T]) Inspect(f func()) *mGenericOutMockName[T] { 92 | if mmName.mock.inspectFuncName != nil { 93 | mmName.mock.t.Fatalf("Inspect function is already set for GenericOutMock.Name") 94 | } 95 | 96 | mmName.mock.inspectFuncName = f 97 | 98 | return mmName 99 | } 100 | 101 | // Return sets up results that will be returned by genericOut.Name 102 | func (mmName *mGenericOutMockName[T]) Return(t1 T) *GenericOutMock[T] { 103 | if mmName.mock.funcName != nil { 104 | mmName.mock.t.Fatalf("GenericOutMock.Name mock is already set by Set") 105 | } 106 | 107 | if mmName.defaultExpectation == nil { 108 | mmName.defaultExpectation = &GenericOutMockNameExpectation[T]{mock: mmName.mock} 109 | } 110 | mmName.defaultExpectation.results = &GenericOutMockNameResults[T]{t1} 111 | mmName.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 112 | return mmName.mock 113 | } 114 | 115 | // Set uses given function f to mock the genericOut.Name method 116 | func (mmName *mGenericOutMockName[T]) Set(f func() (t1 T)) *GenericOutMock[T] { 117 | if mmName.defaultExpectation != nil { 118 | mmName.mock.t.Fatalf("Default expectation is already set for the genericOut.Name method") 119 | } 120 | 121 | if len(mmName.expectations) > 0 { 122 | mmName.mock.t.Fatalf("Some expectations are already set for the genericOut.Name method") 123 | } 124 | 125 | mmName.mock.funcName = f 126 | mmName.mock.funcNameOrigin = minimock.CallerInfo(1) 127 | return mmName.mock 128 | } 129 | 130 | // Times sets number of times genericOut.Name should be invoked 131 | func (mmName *mGenericOutMockName[T]) Times(n uint64) *mGenericOutMockName[T] { 132 | if n == 0 { 133 | mmName.mock.t.Fatalf("Times of GenericOutMock.Name mock can not be zero") 134 | } 135 | mm_atomic.StoreUint64(&mmName.expectedInvocations, n) 136 | mmName.expectedInvocationsOrigin = minimock.CallerInfo(1) 137 | return mmName 138 | } 139 | 140 | func (mmName *mGenericOutMockName[T]) invocationsDone() bool { 141 | if len(mmName.expectations) == 0 && mmName.defaultExpectation == nil && mmName.mock.funcName == nil { 142 | return true 143 | } 144 | 145 | totalInvocations := mm_atomic.LoadUint64(&mmName.mock.afterNameCounter) 146 | expectedInvocations := mm_atomic.LoadUint64(&mmName.expectedInvocations) 147 | 148 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 149 | } 150 | 151 | // Name implements genericOut 152 | func (mmName *GenericOutMock[T]) Name() (t1 T) { 153 | mm_atomic.AddUint64(&mmName.beforeNameCounter, 1) 154 | defer mm_atomic.AddUint64(&mmName.afterNameCounter, 1) 155 | 156 | mmName.t.Helper() 157 | 158 | if mmName.inspectFuncName != nil { 159 | mmName.inspectFuncName() 160 | } 161 | 162 | if mmName.NameMock.defaultExpectation != nil { 163 | mm_atomic.AddUint64(&mmName.NameMock.defaultExpectation.Counter, 1) 164 | 165 | mm_results := mmName.NameMock.defaultExpectation.results 166 | if mm_results == nil { 167 | mmName.t.Fatal("No results are set for the GenericOutMock.Name") 168 | } 169 | return (*mm_results).t1 170 | } 171 | if mmName.funcName != nil { 172 | return mmName.funcName() 173 | } 174 | mmName.t.Fatalf("Unexpected call to GenericOutMock.Name.") 175 | return 176 | } 177 | 178 | // NameAfterCounter returns a count of finished GenericOutMock.Name invocations 179 | func (mmName *GenericOutMock[T]) NameAfterCounter() uint64 { 180 | return mm_atomic.LoadUint64(&mmName.afterNameCounter) 181 | } 182 | 183 | // NameBeforeCounter returns a count of GenericOutMock.Name invocations 184 | func (mmName *GenericOutMock[T]) NameBeforeCounter() uint64 { 185 | return mm_atomic.LoadUint64(&mmName.beforeNameCounter) 186 | } 187 | 188 | // MinimockNameDone returns true if the count of the Name invocations corresponds 189 | // the number of defined expectations 190 | func (m *GenericOutMock[T]) MinimockNameDone() bool { 191 | if m.NameMock.optional { 192 | // Optional methods provide '0 or more' call count restriction. 193 | return true 194 | } 195 | 196 | for _, e := range m.NameMock.expectations { 197 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 198 | return false 199 | } 200 | } 201 | 202 | return m.NameMock.invocationsDone() 203 | } 204 | 205 | // MinimockNameInspect logs each unmet expectation 206 | func (m *GenericOutMock[T]) MinimockNameInspect() { 207 | for _, e := range m.NameMock.expectations { 208 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 209 | m.t.Error("Expected call to GenericOutMock.Name") 210 | } 211 | } 212 | 213 | afterNameCounter := mm_atomic.LoadUint64(&m.afterNameCounter) 214 | // if default expectation was set then invocations count should be greater than zero 215 | if m.NameMock.defaultExpectation != nil && afterNameCounter < 1 { 216 | m.t.Errorf("Expected call to GenericOutMock.Name at\n%s", m.NameMock.defaultExpectation.returnOrigin) 217 | } 218 | // if func was set then invocations count should be greater than zero 219 | if m.funcName != nil && afterNameCounter < 1 { 220 | m.t.Errorf("Expected call to GenericOutMock.Name at\n%s", m.funcNameOrigin) 221 | } 222 | 223 | if !m.NameMock.invocationsDone() && afterNameCounter > 0 { 224 | m.t.Errorf("Expected %d calls to GenericOutMock.Name at\n%s but found %d calls", 225 | mm_atomic.LoadUint64(&m.NameMock.expectedInvocations), m.NameMock.expectedInvocationsOrigin, afterNameCounter) 226 | } 227 | } 228 | 229 | // MinimockFinish checks that all mocked methods have been called the expected number of times 230 | func (m *GenericOutMock[T]) MinimockFinish() { 231 | m.finishOnce.Do(func() { 232 | if !m.minimockDone() { 233 | m.MinimockNameInspect() 234 | } 235 | }) 236 | } 237 | 238 | // MinimockWait waits for all mocked methods to be called the expected number of times 239 | func (m *GenericOutMock[T]) MinimockWait(timeout mm_time.Duration) { 240 | timeoutCh := mm_time.After(timeout) 241 | for { 242 | if m.minimockDone() { 243 | return 244 | } 245 | select { 246 | case <-timeoutCh: 247 | m.MinimockFinish() 248 | return 249 | case <-mm_time.After(10 * mm_time.Millisecond): 250 | } 251 | } 252 | } 253 | 254 | func (m *GenericOutMock[T]) minimockDone() bool { 255 | done := true 256 | return done && 257 | m.MinimockNameDone() 258 | } 259 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package minimock is a command line tool that parses the input Go source file that contains an interface declaration and generates 3 | implementation of this interface that can be used as a mock. 4 | 5 | Main features of minimock 6 | 7 | 1. It's integrated with the standard Go "testing" package 8 | 9 | 2. It supports variadic methods and embedded interfaces 10 | 11 | 3. It's very convenient to use generated mocks in table tests because it implements builder pattern to set up several mocks 12 | 13 | 4. It provides a useful Wait(time.Duration) helper to test concurrent code 14 | 15 | 5. It generates helpers to check if the mocked methods have been called and keeps your tests clean and up to date 16 | 17 | 6. It generates concurrent-safe mock execution counters that you can use in your mocks to implement sophisticated mocks behaviour 18 | 19 | Let's say we have the following interface declaration in github.com/gojuno/minimock/tests package: 20 | 21 | type Formatter interface { 22 | Format(string, ...interface{}) string 23 | } 24 | 25 | Here is how to generate the mock for this interface: 26 | 27 | minimock -i github.com/gojuno/minimock/tests.Formatter -o ./tests/ 28 | 29 | The result file ./tests/formatter_mock_test.go will contain the following code: 30 | 31 | //FormatterMock implements github.com/gojuno/minimock/tests.Formatter 32 | type FormatterMock struct { 33 | t minimock.Tester 34 | 35 | FormatFunc func(p string, p1 ...interface{}) (r string) 36 | FormatCounter uint64 37 | FormatMock mFormatterMockFormat 38 | } 39 | 40 | //NewFormatterMock returns a mock for github.com/gojuno/minimock/tests.Formatter 41 | func NewFormatterMock(t minimock.Tester) *FormatterMock { 42 | m := &FormatterMock{t: t} 43 | 44 | if controller, ok := t.(minimock.MockController); ok { 45 | controller.RegisterMocker(m) 46 | } 47 | 48 | m.FormatMock = mFormatterMockFormat{mock: m} 49 | 50 | return m 51 | } 52 | 53 | type mFormatterMockFormat struct { 54 | mock *FormatterMock 55 | mockExpectations *FormatterMockFormatParams 56 | } 57 | 58 | //FormatterMockFormatParams represents input parameters of the Formatter.Format 59 | type FormatterMockFormatParams struct { 60 | p string 61 | p1 []interface{} 62 | } 63 | 64 | //Expect sets up expected params for the Formatter.Format 65 | func (m *mFormatterMockFormat) Expect(p string, p1 ...interface{}) *mFormatterMockFormat { 66 | m.mockExpectations = &FormatterMockFormatParams{p, p1} 67 | return m 68 | } 69 | 70 | //Return sets up a mock for Formatter.Format to return Return's arguments 71 | func (m *mFormatterMockFormat) Return(r string) *FormatterMock { 72 | m.mock.FormatFunc = func(p string, p1 ...interface{}) string { 73 | return r 74 | } 75 | return m.mock 76 | } 77 | 78 | //Set uses given function f as a mock of Formatter.Format method 79 | func (m *mFormatterMockFormat) Set(f func(p string, p1 ...interface{}) (r string)) *FormatterMock { 80 | m.mock.FormatFunc = f 81 | return m.mock 82 | } 83 | 84 | //Format implements github.com/gojuno/minimock/tests.Formatter interface 85 | func (m *FormatterMock) Format(p string, p1 ...interface{}) (r string) { 86 | defer atomic.AddUint64(&m.FormatCounter, 1) 87 | 88 | if m.FormatMock.mockExpectations != nil { 89 | testify_assert.Equal(m.t, *m.FormatMock.mockExpectations, FormatterMockFormatParams{p, p1}, 90 | "Formatter.Format got unexpected parameters") 91 | 92 | if m.FormatFunc == nil { 93 | m.t.Fatal("No results are set for the FormatterMock.Format") 94 | return 95 | } 96 | } 97 | 98 | if m.FormatFunc == nil { 99 | m.t.Fatal("Unexpected call to FormatterMock.Format") 100 | return 101 | } 102 | 103 | return m.FormatFunc(p, p1...) 104 | } 105 | 106 | //FormatMinimockCounter returns a count of Formatter.Format invocations 107 | func (m *FormatterMock) FormatMinimockCounter() uint64 { 108 | return atomic.LoadUint64(&m.FormatCounter) 109 | } 110 | 111 | //MinimockFinish checks that all mocked methods of the interface have been called at least once 112 | func (m *FormatterMock) MinimockFinish() { 113 | if m.FormatFunc != nil && atomic.LoadUint64(&m.FormatCounter) == 0 { 114 | m.t.Fatal("Expected call to FormatterMock.Format") 115 | } 116 | } 117 | 118 | //MinimockWait waits for all mocked methods to be called at least once 119 | //this method is called by minimock.Controller 120 | func (m *FormatterMock) MinimockWait(timeout time.Duration) { 121 | timeoutCh := time.After(timeout) 122 | for { 123 | ok := true 124 | ok = ok && (m.FormatFunc == nil || atomic.LoadUint64(&m.FormatCounter) > 0) 125 | 126 | if ok { 127 | return 128 | } 129 | 130 | select { 131 | case <-timeoutCh: 132 | 133 | if m.FormatFunc != nil && atomic.LoadUint64(&m.FormatCounter) == 0 { 134 | m.t.Error("Expected call to FormatterMock.Format") 135 | } 136 | 137 | m.t.Fatalf("Some mocks were not called on time: %s", timeout) 138 | return 139 | default: 140 | time.Sleep(time.Millisecond) 141 | } 142 | } 143 | } 144 | 145 | There are several ways to set up a mock 146 | 147 | Setting up a mock using direct assignment: 148 | 149 | formatterMock := NewFormatterMock(mc) 150 | formatterMock.FormatFunc = func(string, ...interface{}) string { 151 | return "minimock" 152 | } 153 | 154 | Setting up a mock using builder pattern and Return method: 155 | 156 | formatterMock := NewFormatterMock(mc).FormatMock.Expect("%s %d", "string", 1).Return("minimock") 157 | 158 | Setting up a mock using builder and Set method: 159 | 160 | formatterMock := NewFormatterMock(mc).FormatMock.Set(func(string, ...interface{}) string { 161 | return "minimock" 162 | }) 163 | 164 | Builder pattern is convenient when you have to mock more than one method of an interface. 165 | Let's say we have an io.ReadCloser interface which has two methods: Read and Close 166 | 167 | type ReadCloser interface { 168 | Read(p []byte) (n int, err error) 169 | Close() error 170 | } 171 | 172 | Then you can set up a mock using just one assignment: 173 | 174 | readCloserMock := NewReadCloserMock(mc).ReadMock.Expect([]byte(1,2,3)).Return(3, nil).CloseMock.Return(nil) 175 | 176 | You can also use invocation counters in your mocks and tests: 177 | 178 | formatterMock := NewFormatterMock(mc) 179 | formatterMock.FormatFunc = func(string, ...interface{}) string { 180 | return fmt.Sprintf("minimock: %d", formatterMock.FormatMinimockCounter()) 181 | } 182 | 183 | minimock.Controller 184 | 185 | When you have to mock multiple dependencies in your test it's recommended to use minimock.Controller and its Finish or Wait methods. 186 | All you have to do is instantiate the Controller and pass it as an argument to the mocks' constructors: 187 | 188 | func TestSomething(t *testing.T) { 189 | mc := minimock.NewController(t) 190 | defer mc.Finish() 191 | 192 | formatterMock := NewFormatterMock(mc) 193 | formatterMock.FormatMock.Return("minimock") 194 | 195 | readCloserMock := NewReadCloserMock(mc) 196 | readCloserMock.ReadMock.Return(5, nil) 197 | 198 | readCloserMock.Read([]byte{}) 199 | formatterMock.Format() 200 | } 201 | 202 | Every mock is registered in the controller so by calling mc.Finish() you can verify that all the registered mocks have been called 203 | within your test. 204 | 205 | Keep your tests clean 206 | 207 | Sometimes we write tons of mocks for our tests but over time the tested code stops using mocked dependencies, 208 | however mocks are still present and being initialized in the test files. So while tested code can shrink, tests are only growing. 209 | To prevent this minimock provides Finish() method that verifies that all your mocks have been called at least once during the test run. 210 | 211 | func TestSomething(t *testing.T) { 212 | mc := minimock.NewController(t) 213 | defer mc.Finish() 214 | 215 | formatterMock := NewFormatterMock(mc) 216 | formatterMock.FormatMock.Return("minimock") 217 | 218 | readCloserMock := NewReadCloserMock(mc) 219 | readCloserMock.ReadMock.Return(5, nil) 220 | 221 | //this test will fail because there are no calls to formatterMock.Format() and readCloserMock.Read() 222 | } 223 | 224 | Testing concurrent code 225 | 226 | Testing concurrent code is tough. Fortunately minimock provides you with the helper method that makes testing concurrent code easy. 227 | Here is how it works: 228 | 229 | func TestSomething(t *testing.T) { 230 | mc := minimock.NewController(t) 231 | 232 | //Wait ensures that all mocked methods have been called within given interval 233 | //if any of the mocked methods have not been called Wait marks test as failed 234 | defer mc.Wait(time.Second) 235 | 236 | formatterMock := NewFormatterMock(mc) 237 | formatterMock.FormatMock.Return("minimock") 238 | 239 | //tested code can run mocked method in a goroutine 240 | go formatterMock.Format("") 241 | } 242 | 243 | Minimock comman line args: 244 | 245 | $ minimock -h 246 | Usage of minimock: 247 | -f string 248 | DEPRECATED: input file or import path of the package that contains interface declaration 249 | -h show this help message 250 | -i string 251 | comma-separated names of the interfaces to mock, i.e fmt.Stringer,io.Reader, use io.* notation to generate mocks for all interfaces in an io package 252 | -o string 253 | destination file name to place the generated mock or path to destination package when multiple interfaces are given 254 | -p string 255 | DEPRECATED: destination package name 256 | -s string 257 | output file name suffix which is added to file names when multiple interfaces are given (default "_mock_test.go") 258 | -t string 259 | DEPRECATED: mock struct name (default Mock) 260 | -withTests 261 | parse *_test.go files in the source package 262 | 263 | */ 264 | package minimock 265 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](https://rawgit.com/gojuno/minimock/master/logo.svg) 2 | [![GoDoc](https://godoc.org/github.com/gojuno/minimock?status.svg)](http://godoc.org/github.com/gojuno/minimock) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/gojuno/minimock)](https://goreportcard.com/report/github.com/gojuno/minimock) 4 | [![Release](https://img.shields.io/github/release/gojuno/minimock.svg)](https://github.com/gojuno/minimock/releases/latest) 5 | [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/avelino/awesome-go#testing) 6 | 7 | 8 | ## Summary 9 | Minimock generates mocks out of Go interface declarations. 10 | 11 | The main features of minimock are: 12 | 13 | * It generates statically typed mocks and helpers. There's no need for type assertions when you use minimock. 14 | * It's fully integrated with the standard Go "testing" package. 15 | * It's ready for Go modules. 16 | * It supports generics. 17 | * It works well with [table driven tests](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) because you can set up mocks for several methods in one line of code using the builder pattern. 18 | * It can generate several mocks in one run. 19 | * It can generate mocks from interface aliases. 20 | * It generates code that passes default set of [golangci-lint](https://github.com/golangci/golangci-lint) checks. 21 | * It puts //go:generate instruction into the generated code, so all you need to do when the source interface is updated is to run the `go generate ./...` command from within the project's directory. 22 | * It makes sure that all mocked methods have been called during the test and keeps your test code clean and up to date. 23 | * It provides When and Then helpers to set up several expectations and results for any method. 24 | * It generates concurrent-safe mocks and mock invocation counters that you can use to manage mock behavior depending on the number of calls. 25 | * It can be used with the [GoUnit](https://github.com/hexdigest/gounit) tool which generates table-driven tests that make use of minimock. 26 | 27 | ## Installation 28 | 29 | If you use go modules please download the [latest binary](https://github.com/gojuno/minimock/releases/latest) 30 | or install minimock from source: 31 | ``` 32 | go install github.com/gojuno/minimock/v3/cmd/minimock@latest 33 | ``` 34 | 35 | If you don't use go modules please find the latest v2.x binary [here](https://github.com/gojuno/minimock/releases) 36 | or install minimock using [v2 branch](https://github.com/gojuno/minimock/tree/v2) 37 | 38 | ## Usage 39 | 40 | ``` 41 | minimock [-i source.interface] [-o output/dir/or/file.go] [-g] 42 | -g don't put go:generate instruction into the generated code 43 | -h show this help message 44 | -i string 45 | comma-separated names of the interfaces to mock, i.e fmt.Stringer,io.Reader 46 | use io.* notation to generate mocks for all interfaces in the "io" package (default "*") 47 | -o string 48 | comma-separated destination file names or packages to put the generated mocks in, 49 | by default the generated mock is placed in the source package directory 50 | -p string 51 | comma-separated package names, 52 | by default the generated package names are taken from the destination directory names 53 | -pr string 54 | mock file prefix 55 | -s string 56 | mock file suffix (default "_mock_test.go") 57 | -gr 58 | changes go:generate line from "//go:generate minimock args..." to 59 | "//go:generate go run github.com/gojuno/minimock/v3/cmd/minimock", 60 | useful while controlling minimock version with go mod 61 | ``` 62 | 63 | Let's say we have the following interface declaration in github.com/gojuno/minimock/tests package: 64 | ```go 65 | type Formatter interface { 66 | Format(string, ...interface{}) string 67 | } 68 | ``` 69 | 70 | This will generate mocks for all interfaces defined in the "tests" package: 71 | 72 | ``` 73 | $ cd ~/go/src/github.com/gojuno/minimock/tests 74 | $ minimock 75 | ``` 76 | 77 | Here is how to generate a mock for the "Formatter" interface only: 78 | 79 | ``` 80 | $ cd ~/go/src/github.com/gojuno/minimock/tests 81 | $ minimock -i Formatter 82 | ``` 83 | 84 | Same using the relative package notation: 85 | 86 | ``` 87 | $ minimock -i ./tests.Formatter 88 | ``` 89 | 90 | Same using the full import path of the source package: 91 | 92 | ``` 93 | $ minimock -i github.com/gojuno/minimock/tests.Formatter -o ./tests/ 94 | ``` 95 | 96 | All the examples above generate ./tests/formatter_mock_test.go file 97 | 98 | 99 | Now it's time to use the generated mock. There are several ways it can be done. 100 | 101 | ### Setting up a mock using the builder pattern and Expect/Return methods: 102 | ```go 103 | mc := minimock.NewController(t) 104 | formatterMock := NewFormatterMock(mc).FormatMock.Expect("hello %s!", "world").Return("hello world!") 105 | ``` 106 | 107 | The builder pattern is convenient when you have more than one method to mock. 108 | Let's say we have an io.ReadCloser interface which has two methods: Read and Close 109 | ```go 110 | type ReadCloser interface { 111 | Read(p []byte) (n int, err error) 112 | Close() error 113 | } 114 | ``` 115 | 116 | We can set up a mock using a simple one-liner: 117 | ```go 118 | mc := minimock.NewController(t) 119 | readCloserMock := NewReadCloserMock(mc).ReadMock.Expect([]byte(1,2,3)).Return(3, nil).CloseMock.Return(nil) 120 | ``` 121 | 122 | But what if we don't want to check all arguments of the read method? 123 | Let's say we just want to check that the second element of the given slice "p" is 2. 124 | This is where "Inspect" helper comes into play: 125 | ```go 126 | mc := minimock.NewController(t) 127 | readCloserMock := NewReadCloserMock(mc).ReadMock.Inspect(func(p []byte){ 128 | assert.Equal(mc, 2, p[1]) 129 | }).Return(3, nil).CloseMock.Return(nil) 130 | 131 | ``` 132 | 133 | ### Setting up a mock using ExpectParams helpers: 134 | 135 | Let's say we have a mocking interface with function that has many arguments: 136 | ```go 137 | type If interface { 138 | Do(intArg int, stringArg string, floatArg float) 139 | } 140 | ``` 141 | 142 | Imagine that you don't want to check all the arguments, just one or two of them. 143 | Then we can use ExpectParams helpers, which are generated for each argument: 144 | ```go 145 | mc := minimock.NewController(t) 146 | ifMock := NewIfMock(mc).DoMock.ExpectIntArgParam1(10).ExpectFloatArgParam3(10.2).Return() 147 | ``` 148 | 149 | ### Setting up a mock using When/Then helpers: 150 | ```go 151 | mc := minimock.NewController(t) 152 | formatterMock := NewFormatterMock(mc) 153 | formatterMock.FormatMock.When("Hello %s!", "world").Then("Hello world!") 154 | formatterMock.FormatMock.When("Hi %s!", "there").Then("Hi there!") 155 | ``` 156 | 157 | alternatively you can use the one-liner: 158 | 159 | ```go 160 | formatterMock = NewFormatterMock(mc).FormatMock.When("Hello %s!", "world").Then("Hello world!").FormatMock.When("Hi %s!", "there").Then("Hi there!") 161 | ``` 162 | 163 | ### Setting up a mock using the Set method: 164 | ```go 165 | mc := minimock.NewController(t) 166 | formatterMock := NewFormatterMock(mc).FormatMock.Set(func(string, ...interface{}) string { 167 | return "minimock" 168 | }) 169 | ``` 170 | 171 | You can also use invocation counters in your mocks and tests: 172 | ```go 173 | mc := minimock.NewController(t) 174 | formatterMock := NewFormatterMock(mc) 175 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { 176 | return fmt.Sprintf("minimock: %d", formatterMock.BeforeFormatCounter()) 177 | }) 178 | ``` 179 | 180 | ### Setting up expected times mock was called: 181 | Imagine you expect mock to be called exactly 10 times. 182 | Then you can set `Times` helper to check how many times mock was invoked. 183 | 184 | ```go 185 | mc := minimock.NewController(t) 186 | formatterMock := NewFormatterMock(mc).FormatMock.Times(10).Expect("hello %s!", "world").Return("hello world!") 187 | ``` 188 | 189 | There are also cases, when you don't know for sure if the mocking method would be called or not. 190 | But you still want to mock it, if it will be called. This is where "Optional" option comes into play: 191 | 192 | ```go 193 | mc := minimock.NewController(t) 194 | formatterMock := NewFormatterMock(mc).FormatMock.Optional().Expect("hello %s!", "world").Return("hello world!") 195 | ``` 196 | 197 | When this option is set, it disables checking the call of mocking method. 198 | 199 | ### Mocking context 200 | Sometimes context gets modified by the time the mocked method is being called. 201 | However, in most cases you don't really care about the exact value of the context argument. 202 | In such cases you can use special `minimock.AnyContext` variable, here are a couple of examples: 203 | 204 | ```go 205 | mc := minimock.NewController(t) 206 | senderMock := NewSenderMock(mc). 207 | SendMock. 208 | When(minimock.AnyContext, "message1").Then(nil). 209 | When(minimock.AnyContext, "message2").Then(errors.New("invalid message")) 210 | ``` 211 | 212 | or using Expect: 213 | 214 | ```go 215 | mc := minimock.NewController(t) 216 | senderMock := NewSenderMock(mc). 217 | SendMock.Expect(minimock.AnyContext, "message").Return(nil) 218 | ``` 219 | 220 | ### Make sure that your mocks are being used 221 | Often we write tons of mocks to test our code but sometimes the tested code stops using mocked dependencies. 222 | You can easily identify this problem by using `minimock.NewController` instead of just `*testing.T`. 223 | Alternatively you can use `mc.Wait` helper if your're testing concurrent code. 224 | These helpers ensure that all your mocks and expectations have been used at least once during the test run. 225 | 226 | ```go 227 | func TestSomething(t *testing.T) { 228 | // it will mark this example test as failed because there are no calls 229 | // to formatterMock.Format() and readCloserMock.Read() below 230 | mc := minimock.NewController(t) 231 | 232 | formatterMock := NewFormatterMock(mc) 233 | formatterMock.FormatMock.Return("minimock") 234 | 235 | readCloserMock := NewReadCloserMock(mc) 236 | readCloserMock.ReadMock.Return(5, nil) 237 | } 238 | ``` 239 | 240 | ### Testing concurrent code 241 | Testing concurrent code is tough. Fortunately minimock.Controller provides you with the helper method that makes testing concurrent code easy. 242 | Here is how it works: 243 | 244 | ```go 245 | func TestSomething(t *testing.T) { 246 | mc := minimock.NewController(t) 247 | 248 | //Wait ensures that all mocked methods have been called within the given time span 249 | //if any of the mocked methods have not been called Wait marks the test as failed 250 | defer mc.Wait(time.Second) 251 | 252 | formatterMock := NewFormatterMock(mc) 253 | formatterMock.FormatMock.Return("minimock") 254 | 255 | //tested code can run the mocked method in a goroutine 256 | go formatterMock.Format("hello world!") 257 | } 258 | ``` 259 | 260 | ## Using GoUnit with minimock 261 | 262 | Writing test is not only mocking the dependencies. Often the test itself contains a lot of boilerplate code. 263 | You can generate test stubs using [GoUnit](https://github.com/hexdigest/gounit) tool which has a nice template that uses minimock. 264 | 265 | Happy mocking! 266 | -------------------------------------------------------------------------------- /tests/formatter_mock_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | 8 | minimock "github.com/gojuno/minimock/v3" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestFormatterMock_ImplementsStringer(t *testing.T) { 14 | tester := NewTesterMock(t) 15 | tester.CleanupMock.Return() 16 | 17 | // this is not required since tester registers MinimockFinish as a cleanup function 18 | // but we want to make sure that existing tests that use MinimockFinish don't break 19 | defer tester.MinimockFinish() 20 | 21 | v := NewFormatterMock(tester) 22 | assert.True(t, reflect.TypeOf(v).Implements(reflect.TypeOf((*Formatter)(nil)).Elem())) 23 | } 24 | 25 | func TestFormatterMock_CleanupIsCalled(t *testing.T) { 26 | tester := NewTesterMock(t) 27 | tester.CleanupMock.Set(t.Cleanup) 28 | tester.ErrorfMock.ExpectFormatParam1("Expected call to FormatterMock.Format at\n%s").Return() 29 | 30 | NewFormatterMock(tester).FormatMock.Return("") 31 | } 32 | 33 | func TestFormatterMock_UnmockedCallFailsTest(t *testing.T) { 34 | var mockCalled bool 35 | tester := NewTesterMock(t) 36 | tester.FatalfMock.Set(func(s string, args ...interface{}) { 37 | assert.Equal(t, "Unexpected call to FormatterMock.Format. %v %v", s) 38 | assert.Equal(t, "this call fails because Format method isn't mocked", args[0]) 39 | 40 | mockCalled = true 41 | }).CleanupMock.Return().HelperMock.Return() 42 | 43 | formatterMock := NewFormatterMock(tester) 44 | dummyFormatter{formatterMock}.Format("this call fails because Format method isn't mocked") 45 | assert.True(t, mockCalled) 46 | } 47 | 48 | func TestFormatterMock_MockedCallSucceeds(t *testing.T) { 49 | tester := NewTesterMock(t).CleanupMock.Return().HelperMock.Return() 50 | 51 | formatterMock := NewFormatterMock(tester) 52 | formatterMock.FormatMock.Set(func(format string, args ...interface{}) string { 53 | return "mock is successfully called" 54 | }) 55 | 56 | df := dummyFormatter{formatterMock} 57 | assert.Equal(t, "mock is successfully called", df.Format("")) 58 | } 59 | 60 | func TestFormatterMock_Wait(t *testing.T) { 61 | tester := NewTesterMock(t).CleanupMock.Return().HelperMock.Return() 62 | 63 | formatterMock := NewFormatterMock(tester) 64 | formatterMock.FormatMock.Set(func(format string, args ...interface{}) string { 65 | return "mock is successfully called from the goroutine" 66 | }) 67 | 68 | go func() { 69 | df := dummyFormatter{formatterMock} 70 | assert.Equal(t, "mock is successfully called from the goroutine", df.Format("")) 71 | }() 72 | 73 | formatterMock.MinimockWait(time.Second) 74 | } 75 | 76 | func TestFormatterMock_Expect(t *testing.T) { 77 | tester := NewTesterMock(t).CleanupMock.Return().HelperMock.Return() 78 | 79 | formatterMock := NewFormatterMock(tester).FormatMock.Expect("Hello", "world", "!").Return("") 80 | 81 | df := dummyFormatter{formatterMock} 82 | df.Format("Hello", "world", "!") 83 | 84 | assert.EqualValues(t, 1, formatterMock.FormatBeforeCounter()) 85 | assert.EqualValues(t, 1, formatterMock.FormatAfterCounter()) 86 | } 87 | 88 | func TestFormatterMock_ExpectDifferentArguments(t *testing.T) { 89 | assert.Panics(t, func() { 90 | tester := NewTesterMock(t).CleanupMock.Return().HelperMock.Return() 91 | 92 | tester.ErrorfMock.Set(func(s string, args ...interface{}) { 93 | assert.Equal(t, "FormatterMock.Format got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", s) 94 | require.Len(t, args, 4) 95 | assert.Equal(t, FormatterMockFormatParams{s1: "expected"}, args[1]) 96 | assert.Equal(t, FormatterMockFormatParams{s1: "actual"}, args[2]) 97 | }) 98 | 99 | tester.FatalMock.Expect("No results are set for the FormatterMock.Format").Return() 100 | 101 | formatterMock := NewFormatterMock(tester) 102 | formatterMock.FormatMock.Expect("expected") 103 | formatterMock.Format("actual") 104 | }) 105 | } 106 | 107 | func TestFormatterMock_ExpectAfterSet(t *testing.T) { 108 | tester := NewTesterMock(t).CleanupMock.Return() 109 | 110 | tester.FatalfMock.Expect("FormatterMock.Format mock is already set by Set").Return() 111 | 112 | formatterMock := NewFormatterMock(tester) 113 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 114 | 115 | formatterMock.FormatMock.Expect("Should not work") 116 | } 117 | 118 | func TestFormatterMock_ExpectAfterWhen(t *testing.T) { 119 | tester := NewTesterMock(t).CleanupMock.Return().HelperMock.Return() 120 | 121 | tester.FatalfMock.Expect("Expectation set by When has same params: %#v", FormatterMockFormatParams{s1: "Should not work", p1: nil}).Return() 122 | 123 | formatterMock := NewFormatterMock(tester) 124 | formatterMock.FormatMock.When("Should not work").Then("") 125 | 126 | formatterMock.Format("Should not work") 127 | 128 | formatterMock.FormatMock.Expect("Should not work") 129 | } 130 | 131 | func TestFormatterMock_Return(t *testing.T) { 132 | tester := NewTesterMock(t).CleanupMock.Return().HelperMock.Return() 133 | 134 | formatterMock := NewFormatterMock(tester).FormatMock.Return("Hello world!") 135 | df := dummyFormatter{formatterMock} 136 | assert.Equal(t, "Hello world!", df.Format("")) 137 | } 138 | 139 | func TestFormatterMock_ReturnAfterSet(t *testing.T) { 140 | tester := NewTesterMock(t) 141 | tester.CleanupMock.Return() 142 | 143 | tester.FatalfMock.Expect("FormatterMock.Format mock is already set by Set").Return() 144 | 145 | formatterMock := NewFormatterMock(tester) 146 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 147 | 148 | formatterMock.FormatMock.Return("Should not work") 149 | } 150 | 151 | func TestFormatterMock_ReturnWithoutExpectForFixedArgsMethod(t *testing.T) { 152 | // Test for issue https://github.com/gojuno/minimock/issues/31 153 | tester := NewTesterMock(t) 154 | tester.CleanupMock.Return() 155 | 156 | tester.ErrorfMock.ExpectFormatParam1("Expected call to FormatterMock.Format at\n%s") 157 | 158 | formatterMock := NewFormatterMock(tester) 159 | formatterMock.FormatMock.Return("") 160 | formatterMock.MinimockFinish() 161 | } 162 | 163 | func TestFormatterMock_Set(t *testing.T) { 164 | tester := NewTesterMock(t) 165 | tester.CleanupMock.Return().HelperMock.Return() 166 | 167 | formatterMock := NewFormatterMock(tester).FormatMock.Set(func(string, ...interface{}) string { 168 | return "set" 169 | }) 170 | 171 | df := dummyFormatter{formatterMock} 172 | assert.Equal(t, "set", df.Format("")) 173 | } 174 | 175 | func TestFormatterMock_SetAfterExpect(t *testing.T) { 176 | tester := NewTesterMock(t) 177 | tester.CleanupMock.Return() 178 | 179 | tester.FatalfMock.Expect("Default expectation is already set for the Formatter.Format method").Return() 180 | 181 | formatterMock := NewFormatterMock(tester).FormatMock.Expect("").Return("") 182 | 183 | //second attempt should fail 184 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 185 | } 186 | 187 | func TestFormatterMock_SetAfterWhen(t *testing.T) { 188 | tester := NewTesterMock(t) 189 | tester.CleanupMock.Return() 190 | 191 | tester.FatalfMock.Expect("Some expectations are already set for the Formatter.Format method").Return() 192 | 193 | formatterMock := NewFormatterMock(tester).FormatMock.When("").Then("") 194 | 195 | //second attempt should fail 196 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 197 | } 198 | 199 | func TestFormatterMockFormat_WhenThen(t *testing.T) { 200 | formatter := NewFormatterMock(t) 201 | defer formatter.MinimockFinish() 202 | 203 | formatter.FormatMock.When("hello %v", "username").Then("hello username") 204 | formatter.FormatMock.When("goodbye %v", "username").Then("goodbye username") 205 | 206 | assert.Equal(t, "hello username", formatter.Format("hello %v", "username")) 207 | assert.Equal(t, "goodbye username", formatter.Format("goodbye %v", "username")) 208 | } 209 | 210 | func TestFormatterMockFormat_WhenAfterSet(t *testing.T) { 211 | tester := NewTesterMock(t) 212 | tester.CleanupMock.Return() 213 | 214 | tester.FatalfMock.Expect("FormatterMock.Format mock is already set by Set").Return() 215 | 216 | formatterMock := NewFormatterMock(tester) 217 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 218 | 219 | formatterMock.FormatMock.When("Should not work") 220 | } 221 | 222 | func TestFormatterMock_MinimockFormatDone(t *testing.T) { 223 | tester := NewTesterMock(t). 224 | CleanupMock.Return() 225 | 226 | formatterMock := NewFormatterMock(tester) 227 | 228 | formatterMock.FormatMock.expectations = []*FormatterMockFormatExpectation{{ 229 | params: &FormatterMockFormatParams{}, 230 | }} 231 | assert.False(t, formatterMock.MinimockFormatDone()) 232 | } 233 | 234 | func TestFormatterMock_MinimockFinish(t *testing.T) { 235 | tester := NewTesterMock(t) 236 | tester.CleanupMock.Return() 237 | 238 | tester.ErrorfMock.ExpectFormatParam1("Expected call to FormatterMock.Format at\n%s").Return() 239 | 240 | formatterMock := NewFormatterMock(tester) 241 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 242 | 243 | formatterMock.MinimockFinish() 244 | } 245 | 246 | func TestFormatterMock_MinimockFinish_WithNoMetExpectations(t *testing.T) { 247 | tester := NewTesterMock(t) 248 | tester.CleanupMock.Return() 249 | 250 | tester.ErrorfMock.ExpectFormatParam1("Expected call to FormatterMock.Format at\n%s with params: %#v") 251 | 252 | formatterMock := NewFormatterMock(tester) 253 | formatterMock.FormatMock.Expect("a").Return("a") 254 | formatterMock.FormatMock.When("b").Then("b") 255 | 256 | formatterMock.MinimockFinish() 257 | } 258 | 259 | func TestFormatterMock_MinimockWait(t *testing.T) { 260 | tester := NewTesterMock(t) 261 | tester.CleanupMock.Return() 262 | 263 | tester.ErrorfMock. 264 | ExpectFormatParam1("Expected call to FormatterMock.Format at\n%s").Return() 265 | 266 | formatterMock := NewFormatterMock(tester) 267 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 268 | 269 | formatterMock.MinimockWait(time.Millisecond) 270 | } 271 | 272 | // Verifies that Calls() doesn't return nil if no calls were made 273 | func TestFormatterMock_CallsNotNil(t *testing.T) { 274 | tester := NewTesterMock(t) 275 | tester.CleanupMock.Return() 276 | 277 | formatterMock := NewFormatterMock(tester) 278 | calls := formatterMock.FormatMock.Calls() 279 | 280 | assert.NotNil(t, calls) 281 | assert.Empty(t, calls) 282 | } 283 | 284 | // Verifies that Calls() returns the correct call args in the expected order 285 | func TestFormatterMock_Calls(t *testing.T) { 286 | tester := NewTesterMock(t) 287 | tester.CleanupMock.Return().HelperMock.Return() 288 | 289 | // Arguments used for each mock call 290 | expected := []*FormatterMockFormatParams{ 291 | {"a1", []interface{}{}}, 292 | {"b1", []interface{}{"b2"}}, 293 | {"c1", []interface{}{"c2", "c3"}}, 294 | {"d1", []interface{}{"d2", "d3", "d4"}}, 295 | } 296 | 297 | formatterMock := NewFormatterMock(tester) 298 | 299 | for _, p := range expected { 300 | formatterMock.FormatMock.Expect(p.s1, p.p1...).Return("") 301 | formatterMock.Format(p.s1, p.p1...) 302 | } 303 | 304 | assert.Equal(t, expected, formatterMock.FormatMock.Calls()) 305 | } 306 | 307 | // Verifies that Calls() returns a new shallow copy of the params list each time 308 | func TestFormatterMock_CallsReturnsCopy(t *testing.T) { 309 | tester := NewTesterMock(t) 310 | tester.CleanupMock.Return().HelperMock.Return() 311 | 312 | expected := []*FormatterMockFormatParams{ 313 | {"a1", []interface{}{"a1"}}, 314 | {"b1", []interface{}{"b2"}}, 315 | } 316 | 317 | formatterMock := NewFormatterMock(tester) 318 | callHistory := [][]*FormatterMockFormatParams{} 319 | 320 | for _, p := range expected { 321 | formatterMock.FormatMock.Expect(p.s1, p.p1...).Return("") 322 | formatterMock.Format(p.s1, p.p1...) 323 | callHistory = append(callHistory, formatterMock.FormatMock.Calls()) 324 | } 325 | 326 | assert.Equal(t, len(expected), len(callHistory)) 327 | 328 | for i, c := range callHistory { 329 | assert.Equal(t, i+1, len(c)) 330 | } 331 | } 332 | 333 | type dummyFormatter struct { 334 | Formatter 335 | } 336 | 337 | type dummyMockController struct { 338 | minimock.MockController 339 | registerCounter int 340 | } 341 | 342 | func (dmc *dummyMockController) RegisterMocker(m minimock.Mocker) { 343 | dmc.registerCounter++ 344 | } 345 | 346 | func (dmc *dummyMockController) Cleanup(f func()) { 347 | f() 348 | } 349 | 350 | func TestFormatterMock_RegistersMocker(t *testing.T) { 351 | mockController := &dummyMockController{} 352 | 353 | NewFormatterMock(mockController) 354 | assert.Equal(t, 1, mockController.registerCounter) 355 | } 356 | -------------------------------------------------------------------------------- /tests/formatter_with_custom_name_mock_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestCustomFormatterNameMock_ImplementsStringer(t *testing.T) { 13 | tester := NewTesterMock(t) 14 | tester.CleanupMock.Return() 15 | 16 | v := NewCustomFormatterNameMock(tester) 17 | assert.True(t, reflect.TypeOf(v).Implements(reflect.TypeOf((*Formatter)(nil)).Elem())) 18 | } 19 | 20 | func TestCustomFormatterNameMock_UnmockedCallFailsTest(t *testing.T) { 21 | var mockCalled bool 22 | tester := NewTesterMock(t) 23 | tester.CleanupMock.Return().HelperMock.Return() 24 | 25 | tester.FatalfMock.Set(func(s string, args ...interface{}) { 26 | assert.Equal(t, "Unexpected call to CustomFormatterNameMock.Format. %v %v", s) 27 | assert.Equal(t, "this call fails because Format method isn't mocked", args[0]) 28 | 29 | mockCalled = true 30 | }) 31 | 32 | defer tester.MinimockFinish() 33 | 34 | formatterMock := NewCustomFormatterNameMock(tester) 35 | dummyFormatter{formatterMock}.Format("this call fails because Format method isn't mocked") 36 | assert.True(t, mockCalled) 37 | } 38 | 39 | func TestCustomFormatterNameMock_MockedCallSucceeds(t *testing.T) { 40 | tester := NewTesterMock(t) 41 | tester.CleanupMock.Return().HelperMock.Return() 42 | 43 | formatterMock := NewCustomFormatterNameMock(tester) 44 | formatterMock.FormatMock.Set(func(format string, args ...interface{}) string { 45 | return "mock is successfully called" 46 | }) 47 | defer tester.MinimockFinish() 48 | 49 | df := dummyFormatter{formatterMock} 50 | assert.Equal(t, "mock is successfully called", df.Format("")) 51 | } 52 | 53 | func TestCustomFormatterNameMock_Wait(t *testing.T) { 54 | tester := NewTesterMock(t) 55 | tester.CleanupMock.Return().HelperMock.Return() 56 | 57 | formatterMock := NewCustomFormatterNameMock(tester) 58 | formatterMock.FormatMock.Set(func(format string, args ...interface{}) string { 59 | return "mock is successfully called from the goroutine" 60 | }) 61 | 62 | go func() { 63 | df := dummyFormatter{formatterMock} 64 | assert.Equal(t, "mock is successfully called from the goroutine", df.Format("")) 65 | }() 66 | 67 | formatterMock.MinimockWait(time.Second) 68 | } 69 | 70 | func TestCustomFormatterNameMock_Expect(t *testing.T) { 71 | tester := NewTesterMock(t) 72 | tester.CleanupMock.Return().HelperMock.Return() 73 | 74 | formatterMock := NewCustomFormatterNameMock(tester).FormatMock.Expect("Hello", "world", "!").Return("") 75 | 76 | df := dummyFormatter{formatterMock} 77 | df.Format("Hello", "world", "!") 78 | 79 | assert.EqualValues(t, 1, formatterMock.FormatBeforeCounter()) 80 | assert.EqualValues(t, 1, formatterMock.FormatAfterCounter()) 81 | } 82 | 83 | func TestCustomFormatterNameMock_ExpectDifferentArguments(t *testing.T) { 84 | assert.Panics(t, func() { 85 | tester := NewTesterMock(t) 86 | tester.CleanupMock.Return().HelperMock.Return() 87 | 88 | defer tester.MinimockFinish() 89 | 90 | tester.ErrorfMock.Set(func(s string, args ...interface{}) { 91 | assert.Equal(t, "CustomFormatterNameMock.Format got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", s) 92 | require.Len(t, args, 4) 93 | assert.Equal(t, CustomFormatterNameMockFormatParams{s1: "expected"}, args[1]) 94 | assert.Equal(t, CustomFormatterNameMockFormatParams{s1: "actual"}, args[2]) 95 | }) 96 | 97 | tester.FatalMock.Expect("No results are set for the CustomFormatterNameMock.Format").Return() 98 | 99 | formatterMock := NewCustomFormatterNameMock(tester) 100 | formatterMock.FormatMock.Expect("expected") 101 | formatterMock.Format("actual") 102 | }) 103 | } 104 | 105 | func TestCustomFormatterNameMock_ExpectAfterSet(t *testing.T) { 106 | tester := NewTesterMock(t) 107 | defer tester.MinimockFinish() 108 | 109 | tester.CleanupMock.Return() 110 | 111 | tester.FatalfMock.Expect("CustomFormatterNameMock.Format mock is already set by Set").Return() 112 | 113 | formatterMock := NewCustomFormatterNameMock(tester) 114 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 115 | 116 | formatterMock.FormatMock.Expect("Should not work") 117 | } 118 | 119 | func TestCustomFormatterNameMock_ExpectAfterWhen(t *testing.T) { 120 | tester := NewTesterMock(t) 121 | defer tester.MinimockFinish() 122 | 123 | tester.CleanupMock.Return().HelperMock.Return() 124 | 125 | tester.FatalfMock.Expect("Expectation set by When has same params: %#v", CustomFormatterNameMockFormatParams{s1: "Should not work", p1: nil}).Return() 126 | 127 | formatterMock := NewCustomFormatterNameMock(tester) 128 | formatterMock.FormatMock.When("Should not work").Then("") 129 | 130 | formatterMock.Format("Should not work") 131 | 132 | formatterMock.FormatMock.Expect("Should not work") 133 | } 134 | 135 | func TestCustomFormatterNameMock_Return(t *testing.T) { 136 | tester := NewTesterMock(t) 137 | tester.CleanupMock.Return().HelperMock.Return() 138 | 139 | formatterMock := NewCustomFormatterNameMock(tester).FormatMock.Return("Hello world!") 140 | df := dummyFormatter{formatterMock} 141 | assert.Equal(t, "Hello world!", df.Format("")) 142 | } 143 | 144 | func TestCustomFormatterNameMock_ReturnAfterSet(t *testing.T) { 145 | tester := NewTesterMock(t) 146 | defer tester.MinimockFinish() 147 | 148 | tester.CleanupMock.Return() 149 | 150 | tester.FatalfMock.Expect("CustomFormatterNameMock.Format mock is already set by Set").Return() 151 | 152 | formatterMock := NewCustomFormatterNameMock(tester) 153 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 154 | 155 | formatterMock.FormatMock.Return("Should not work") 156 | } 157 | 158 | func TestCustomFormatterNameMock_ReturnWithoutExpectForFixedArgsMethod(t *testing.T) { 159 | // Test for issue https://github.com/gojuno/minimock/issues/31 160 | 161 | tester := NewTesterMock(t) 162 | defer tester.MinimockFinish() 163 | 164 | tester.CleanupMock.Return() 165 | 166 | tester.ErrorfMock.ExpectFormatParam1("Expected call to CustomFormatterNameMock.Format at\n%s"). 167 | Return() 168 | 169 | formatterMock := NewCustomFormatterNameMock(tester) 170 | formatterMock.FormatMock.Return("") 171 | formatterMock.MinimockFinish() 172 | } 173 | 174 | func TestCustomFormatterNameMock_Set(t *testing.T) { 175 | tester := NewTesterMock(t) 176 | tester.CleanupMock.Return().HelperMock.Return() 177 | 178 | formatterMock := NewCustomFormatterNameMock(tester).FormatMock.Set(func(string, ...interface{}) string { 179 | return "set" 180 | }) 181 | 182 | df := dummyFormatter{formatterMock} 183 | assert.Equal(t, "set", df.Format("")) 184 | } 185 | 186 | func TestCustomFormatterNameMock_SetAfterExpect(t *testing.T) { 187 | tester := NewTesterMock(t) 188 | defer tester.MinimockFinish() 189 | 190 | tester.CleanupMock.Return() 191 | 192 | tester.FatalfMock.Expect("Default expectation is already set for the Formatter.Format method").Return() 193 | 194 | formatterMock := NewCustomFormatterNameMock(tester).FormatMock.Expect("").Return("") 195 | 196 | //second attempt should fail 197 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 198 | } 199 | 200 | func TestCustomFormatterNameMock_SetAfterWhen(t *testing.T) { 201 | tester := NewTesterMock(t) 202 | defer tester.MinimockFinish() 203 | 204 | tester.CleanupMock.Return() 205 | 206 | tester.FatalfMock.Expect("Some expectations are already set for the Formatter.Format method").Return() 207 | 208 | formatterMock := NewCustomFormatterNameMock(tester).FormatMock.When("").Then("") 209 | 210 | //second attempt should fail 211 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 212 | } 213 | 214 | func TestCustomFormatterNameMockFormat_WhenThen(t *testing.T) { 215 | formatter := NewCustomFormatterNameMock(t) 216 | defer formatter.MinimockFinish() 217 | 218 | formatter.FormatMock.When("hello %v", "username").Then("hello username") 219 | formatter.FormatMock.When("goodbye %v", "username").Then("goodbye username") 220 | 221 | assert.Equal(t, "hello username", formatter.Format("hello %v", "username")) 222 | assert.Equal(t, "goodbye username", formatter.Format("goodbye %v", "username")) 223 | } 224 | 225 | func TestCustomFormatterNameMockFormat_WhenAfterSet(t *testing.T) { 226 | tester := NewTesterMock(t) 227 | defer tester.MinimockFinish() 228 | 229 | tester.CleanupMock.Return() 230 | 231 | tester.FatalfMock.Expect("CustomFormatterNameMock.Format mock is already set by Set").Return() 232 | 233 | formatterMock := NewCustomFormatterNameMock(tester) 234 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 235 | 236 | formatterMock.FormatMock.When("Should not work") 237 | } 238 | 239 | func TestCustomFormatterNameMock_MinimockFormatDone(t *testing.T) { 240 | tester := NewTesterMock(t) 241 | tester.CleanupMock.Return() 242 | 243 | formatterMock := NewCustomFormatterNameMock(tester) 244 | 245 | formatterMock.FormatMock.expectations = []*CustomFormatterNameMockFormatExpectation{{}} 246 | assert.False(t, formatterMock.MinimockFormatDone()) 247 | 248 | formatterMock = NewCustomFormatterNameMock(tester) 249 | formatterMock.FormatMock.defaultExpectation = &CustomFormatterNameMockFormatExpectation{} 250 | assert.False(t, formatterMock.MinimockFormatDone()) 251 | } 252 | 253 | func TestCustomFormatterNameMock_MinimockFinish(t *testing.T) { 254 | tester := NewTesterMock(t) 255 | defer tester.MinimockFinish() 256 | 257 | tester.CleanupMock.Return() 258 | 259 | tester.ErrorfMock.ExpectFormatParam1("Expected call to CustomFormatterNameMock.Format at\n%s"). 260 | Return() 261 | 262 | formatterMock := NewCustomFormatterNameMock(tester) 263 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 264 | 265 | formatterMock.MinimockFinish() 266 | } 267 | 268 | func TestCustomFormatterNameMock_MinimockFinish_WithNoMetExpectations(t *testing.T) { 269 | tester := NewTesterMock(t) 270 | defer tester.MinimockFinish() 271 | 272 | tester.CleanupMock.Return() 273 | 274 | tester.ErrorfMock.ExpectFormatParam1("Expected call to CustomFormatterNameMock.Format at\n%s with params: %#v") 275 | 276 | formatterMock := NewCustomFormatterNameMock(tester) 277 | formatterMock.FormatMock.Expect("a").Return("a") 278 | formatterMock.FormatMock.When("b").Then("b") 279 | 280 | formatterMock.MinimockFinish() 281 | } 282 | 283 | func TestCustomFormatterNameMock_MinimockWait(t *testing.T) { 284 | tester := NewTesterMock(t) 285 | defer tester.MinimockFinish() 286 | 287 | tester.CleanupMock.Return() 288 | 289 | tester.ErrorfMock.ExpectFormatParam1("Expected call to CustomFormatterNameMock.Format at\n%s"). 290 | Return() 291 | 292 | formatterMock := NewCustomFormatterNameMock(tester) 293 | formatterMock.FormatMock.Set(func(string, ...interface{}) string { return "" }) 294 | 295 | formatterMock.MinimockWait(time.Millisecond) 296 | } 297 | 298 | // Verifies that Calls() doesn't return nil if no calls were made 299 | func TestCustomFormatterNameMock_CallsNotNil(t *testing.T) { 300 | tester := NewTesterMock(t) 301 | defer tester.MinimockFinish() 302 | 303 | tester.CleanupMock.Return() 304 | 305 | formatterMock := NewCustomFormatterNameMock(tester) 306 | calls := formatterMock.FormatMock.Calls() 307 | 308 | assert.NotNil(t, calls) 309 | assert.Empty(t, calls) 310 | } 311 | 312 | // Verifies that Calls() returns the correct call args in the expected order 313 | func TestCustomFormatterNameMock_Calls(t *testing.T) { 314 | tester := NewTesterMock(t) 315 | defer tester.MinimockFinish() 316 | 317 | tester.CleanupMock.Return().HelperMock.Return() 318 | 319 | // Arguments used for each mock call 320 | expected := []*CustomFormatterNameMockFormatParams{ 321 | {"a1", []interface{}{}}, 322 | {"b1", []interface{}{"b2"}}, 323 | {"c1", []interface{}{"c2", "c3"}}, 324 | {"d1", []interface{}{"d2", "d3", "d4"}}, 325 | } 326 | 327 | formatterMock := NewCustomFormatterNameMock(tester) 328 | 329 | for _, p := range expected { 330 | formatterMock.FormatMock.Expect(p.s1, p.p1...).Return("") 331 | formatterMock.Format(p.s1, p.p1...) 332 | } 333 | 334 | assert.Equal(t, expected, formatterMock.FormatMock.Calls()) 335 | } 336 | 337 | // Verifies that Calls() returns a new shallow copy of the params list each time 338 | func TestCustomFormatterNameMock_CallsReturnsCopy(t *testing.T) { 339 | tester := NewTesterMock(t) 340 | defer tester.MinimockFinish() 341 | 342 | tester.CleanupMock.Return().HelperMock.Return() 343 | 344 | expected := []*CustomFormatterNameMockFormatParams{ 345 | {"a1", []interface{}{"a1"}}, 346 | {"b1", []interface{}{"b2"}}, 347 | } 348 | 349 | formatterMock := NewCustomFormatterNameMock(tester) 350 | callHistory := [][]*CustomFormatterNameMockFormatParams{} 351 | 352 | for _, p := range expected { 353 | formatterMock.FormatMock.Expect(p.s1, p.p1...).Return("") 354 | formatterMock.Format(p.s1, p.p1...) 355 | callHistory = append(callHistory, formatterMock.FormatMock.Calls()) 356 | } 357 | 358 | assert.Equal(t, len(expected), len(callHistory)) 359 | 360 | for i, c := range callHistory { 361 | assert.Equal(t, i+1, len(c)) 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /tests/generic_in.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate minimock -i github.com/gojuno/minimock/v3/tests.genericIn -o generic_in.go -n GenericInMock -p tests 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | ) 14 | 15 | // GenericInMock implements genericIn 16 | type GenericInMock[T any] struct { 17 | t minimock.Tester 18 | finishOnce sync.Once 19 | 20 | funcName func(t1 T) 21 | funcNameOrigin string 22 | inspectFuncName func(t1 T) 23 | afterNameCounter uint64 24 | beforeNameCounter uint64 25 | NameMock mGenericInMockName[T] 26 | } 27 | 28 | // NewGenericInMock returns a mock for genericIn 29 | func NewGenericInMock[T any](t minimock.Tester) *GenericInMock[T] { 30 | m := &GenericInMock[T]{t: t} 31 | 32 | if controller, ok := t.(minimock.MockController); ok { 33 | controller.RegisterMocker(m) 34 | } 35 | 36 | m.NameMock = mGenericInMockName[T]{mock: m} 37 | m.NameMock.callArgs = []*GenericInMockNameParams[T]{} 38 | 39 | t.Cleanup(m.MinimockFinish) 40 | 41 | return m 42 | } 43 | 44 | type mGenericInMockName[T any] struct { 45 | optional bool 46 | mock *GenericInMock[T] 47 | defaultExpectation *GenericInMockNameExpectation[T] 48 | expectations []*GenericInMockNameExpectation[T] 49 | 50 | callArgs []*GenericInMockNameParams[T] 51 | mutex sync.RWMutex 52 | 53 | expectedInvocations uint64 54 | expectedInvocationsOrigin string 55 | } 56 | 57 | // GenericInMockNameExpectation specifies expectation struct of the genericIn.Name 58 | type GenericInMockNameExpectation[T any] struct { 59 | mock *GenericInMock[T] 60 | params *GenericInMockNameParams[T] 61 | paramPtrs *GenericInMockNameParamPtrs[T] 62 | expectationOrigins GenericInMockNameExpectationOrigins 63 | 64 | returnOrigin string 65 | Counter uint64 66 | } 67 | 68 | // GenericInMockNameParams contains parameters of the genericIn.Name 69 | type GenericInMockNameParams[T any] struct { 70 | t1 T 71 | } 72 | 73 | // GenericInMockNameParamPtrs contains pointers to parameters of the genericIn.Name 74 | type GenericInMockNameParamPtrs[T any] struct { 75 | t1 *T 76 | } 77 | 78 | // GenericInMockNameOrigins contains origins of expectations of the genericIn.Name 79 | type GenericInMockNameExpectationOrigins struct { 80 | origin string 81 | originT1 string 82 | } 83 | 84 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 85 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 86 | // Optional() makes method check to work in '0 or more' mode. 87 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 88 | // catch the problems when the expected method call is totally skipped during test run. 89 | func (mmName *mGenericInMockName[T]) Optional() *mGenericInMockName[T] { 90 | mmName.optional = true 91 | return mmName 92 | } 93 | 94 | // Expect sets up expected params for genericIn.Name 95 | func (mmName *mGenericInMockName[T]) Expect(t1 T) *mGenericInMockName[T] { 96 | if mmName.mock.funcName != nil { 97 | mmName.mock.t.Fatalf("GenericInMock.Name mock is already set by Set") 98 | } 99 | 100 | if mmName.defaultExpectation == nil { 101 | mmName.defaultExpectation = &GenericInMockNameExpectation[T]{} 102 | } 103 | 104 | if mmName.defaultExpectation.paramPtrs != nil { 105 | mmName.mock.t.Fatalf("GenericInMock.Name mock is already set by ExpectParams functions") 106 | } 107 | 108 | mmName.defaultExpectation.params = &GenericInMockNameParams[T]{t1} 109 | mmName.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) 110 | for _, e := range mmName.expectations { 111 | if minimock.Equal(e.params, mmName.defaultExpectation.params) { 112 | mmName.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmName.defaultExpectation.params) 113 | } 114 | } 115 | 116 | return mmName 117 | } 118 | 119 | // ExpectT1Param1 sets up expected param t1 for genericIn.Name 120 | func (mmName *mGenericInMockName[T]) ExpectT1Param1(t1 T) *mGenericInMockName[T] { 121 | if mmName.mock.funcName != nil { 122 | mmName.mock.t.Fatalf("GenericInMock.Name mock is already set by Set") 123 | } 124 | 125 | if mmName.defaultExpectation == nil { 126 | mmName.defaultExpectation = &GenericInMockNameExpectation[T]{} 127 | } 128 | 129 | if mmName.defaultExpectation.params != nil { 130 | mmName.mock.t.Fatalf("GenericInMock.Name mock is already set by Expect") 131 | } 132 | 133 | if mmName.defaultExpectation.paramPtrs == nil { 134 | mmName.defaultExpectation.paramPtrs = &GenericInMockNameParamPtrs[T]{} 135 | } 136 | mmName.defaultExpectation.paramPtrs.t1 = &t1 137 | mmName.defaultExpectation.expectationOrigins.originT1 = minimock.CallerInfo(1) 138 | 139 | return mmName 140 | } 141 | 142 | // Inspect accepts an inspector function that has same arguments as the genericIn.Name 143 | func (mmName *mGenericInMockName[T]) Inspect(f func(t1 T)) *mGenericInMockName[T] { 144 | if mmName.mock.inspectFuncName != nil { 145 | mmName.mock.t.Fatalf("Inspect function is already set for GenericInMock.Name") 146 | } 147 | 148 | mmName.mock.inspectFuncName = f 149 | 150 | return mmName 151 | } 152 | 153 | // Return sets up results that will be returned by genericIn.Name 154 | func (mmName *mGenericInMockName[T]) Return() *GenericInMock[T] { 155 | if mmName.mock.funcName != nil { 156 | mmName.mock.t.Fatalf("GenericInMock.Name mock is already set by Set") 157 | } 158 | 159 | if mmName.defaultExpectation == nil { 160 | mmName.defaultExpectation = &GenericInMockNameExpectation[T]{mock: mmName.mock} 161 | } 162 | 163 | mmName.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 164 | return mmName.mock 165 | } 166 | 167 | // Set uses given function f to mock the genericIn.Name method 168 | func (mmName *mGenericInMockName[T]) Set(f func(t1 T)) *GenericInMock[T] { 169 | if mmName.defaultExpectation != nil { 170 | mmName.mock.t.Fatalf("Default expectation is already set for the genericIn.Name method") 171 | } 172 | 173 | if len(mmName.expectations) > 0 { 174 | mmName.mock.t.Fatalf("Some expectations are already set for the genericIn.Name method") 175 | } 176 | 177 | mmName.mock.funcName = f 178 | mmName.mock.funcNameOrigin = minimock.CallerInfo(1) 179 | return mmName.mock 180 | } 181 | 182 | // When sets expectation for the genericIn.Name which will trigger the result defined by the following 183 | // Then helper 184 | func (mmName *mGenericInMockName[T]) When(t1 T) *GenericInMockNameExpectation[T] { 185 | if mmName.mock.funcName != nil { 186 | mmName.mock.t.Fatalf("GenericInMock.Name mock is already set by Set") 187 | } 188 | 189 | expectation := &GenericInMockNameExpectation[T]{ 190 | mock: mmName.mock, 191 | params: &GenericInMockNameParams[T]{t1}, 192 | expectationOrigins: GenericInMockNameExpectationOrigins{origin: minimock.CallerInfo(1)}, 193 | } 194 | mmName.expectations = append(mmName.expectations, expectation) 195 | return expectation 196 | } 197 | 198 | // Then sets up genericIn.Name return parameters for the expectation previously defined by the When method 199 | 200 | func (e *GenericInMockNameExpectation[T]) Then() *GenericInMock[T] { 201 | return e.mock 202 | } 203 | 204 | // Times sets number of times genericIn.Name should be invoked 205 | func (mmName *mGenericInMockName[T]) Times(n uint64) *mGenericInMockName[T] { 206 | if n == 0 { 207 | mmName.mock.t.Fatalf("Times of GenericInMock.Name mock can not be zero") 208 | } 209 | mm_atomic.StoreUint64(&mmName.expectedInvocations, n) 210 | mmName.expectedInvocationsOrigin = minimock.CallerInfo(1) 211 | return mmName 212 | } 213 | 214 | func (mmName *mGenericInMockName[T]) invocationsDone() bool { 215 | if len(mmName.expectations) == 0 && mmName.defaultExpectation == nil && mmName.mock.funcName == nil { 216 | return true 217 | } 218 | 219 | totalInvocations := mm_atomic.LoadUint64(&mmName.mock.afterNameCounter) 220 | expectedInvocations := mm_atomic.LoadUint64(&mmName.expectedInvocations) 221 | 222 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 223 | } 224 | 225 | // Name implements genericIn 226 | func (mmName *GenericInMock[T]) Name(t1 T) { 227 | mm_atomic.AddUint64(&mmName.beforeNameCounter, 1) 228 | defer mm_atomic.AddUint64(&mmName.afterNameCounter, 1) 229 | 230 | mmName.t.Helper() 231 | 232 | if mmName.inspectFuncName != nil { 233 | mmName.inspectFuncName(t1) 234 | } 235 | 236 | mm_params := GenericInMockNameParams[T]{t1} 237 | 238 | // Record call args 239 | mmName.NameMock.mutex.Lock() 240 | mmName.NameMock.callArgs = append(mmName.NameMock.callArgs, &mm_params) 241 | mmName.NameMock.mutex.Unlock() 242 | 243 | for _, e := range mmName.NameMock.expectations { 244 | if minimock.Equal(*e.params, mm_params) { 245 | mm_atomic.AddUint64(&e.Counter, 1) 246 | return 247 | } 248 | } 249 | 250 | if mmName.NameMock.defaultExpectation != nil { 251 | mm_atomic.AddUint64(&mmName.NameMock.defaultExpectation.Counter, 1) 252 | mm_want := mmName.NameMock.defaultExpectation.params 253 | mm_want_ptrs := mmName.NameMock.defaultExpectation.paramPtrs 254 | 255 | mm_got := GenericInMockNameParams[T]{t1} 256 | 257 | if mm_want_ptrs != nil { 258 | 259 | if mm_want_ptrs.t1 != nil && !minimock.Equal(*mm_want_ptrs.t1, mm_got.t1) { 260 | mmName.t.Errorf("GenericInMock.Name got unexpected parameter t1, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 261 | mmName.NameMock.defaultExpectation.expectationOrigins.originT1, *mm_want_ptrs.t1, mm_got.t1, minimock.Diff(*mm_want_ptrs.t1, mm_got.t1)) 262 | } 263 | 264 | } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { 265 | mmName.t.Errorf("GenericInMock.Name got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 266 | mmName.NameMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) 267 | } 268 | 269 | return 270 | 271 | } 272 | if mmName.funcName != nil { 273 | mmName.funcName(t1) 274 | return 275 | } 276 | mmName.t.Fatalf("Unexpected call to GenericInMock.Name. %v", t1) 277 | 278 | } 279 | 280 | // NameAfterCounter returns a count of finished GenericInMock.Name invocations 281 | func (mmName *GenericInMock[T]) NameAfterCounter() uint64 { 282 | return mm_atomic.LoadUint64(&mmName.afterNameCounter) 283 | } 284 | 285 | // NameBeforeCounter returns a count of GenericInMock.Name invocations 286 | func (mmName *GenericInMock[T]) NameBeforeCounter() uint64 { 287 | return mm_atomic.LoadUint64(&mmName.beforeNameCounter) 288 | } 289 | 290 | // Calls returns a list of arguments used in each call to GenericInMock.Name. 291 | // The list is in the same order as the calls were made (i.e. recent calls have a higher index) 292 | func (mmName *mGenericInMockName[T]) Calls() []*GenericInMockNameParams[T] { 293 | mmName.mutex.RLock() 294 | 295 | argCopy := make([]*GenericInMockNameParams[T], len(mmName.callArgs)) 296 | copy(argCopy, mmName.callArgs) 297 | 298 | mmName.mutex.RUnlock() 299 | 300 | return argCopy 301 | } 302 | 303 | // MinimockNameDone returns true if the count of the Name invocations corresponds 304 | // the number of defined expectations 305 | func (m *GenericInMock[T]) MinimockNameDone() bool { 306 | if m.NameMock.optional { 307 | // Optional methods provide '0 or more' call count restriction. 308 | return true 309 | } 310 | 311 | for _, e := range m.NameMock.expectations { 312 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 313 | return false 314 | } 315 | } 316 | 317 | return m.NameMock.invocationsDone() 318 | } 319 | 320 | // MinimockNameInspect logs each unmet expectation 321 | func (m *GenericInMock[T]) MinimockNameInspect() { 322 | for _, e := range m.NameMock.expectations { 323 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 324 | m.t.Errorf("Expected call to GenericInMock.Name at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) 325 | } 326 | } 327 | 328 | afterNameCounter := mm_atomic.LoadUint64(&m.afterNameCounter) 329 | // if default expectation was set then invocations count should be greater than zero 330 | if m.NameMock.defaultExpectation != nil && afterNameCounter < 1 { 331 | if m.NameMock.defaultExpectation.params == nil { 332 | m.t.Errorf("Expected call to GenericInMock.Name at\n%s", m.NameMock.defaultExpectation.returnOrigin) 333 | } else { 334 | m.t.Errorf("Expected call to GenericInMock.Name at\n%s with params: %#v", m.NameMock.defaultExpectation.expectationOrigins.origin, *m.NameMock.defaultExpectation.params) 335 | } 336 | } 337 | // if func was set then invocations count should be greater than zero 338 | if m.funcName != nil && afterNameCounter < 1 { 339 | m.t.Errorf("Expected call to GenericInMock.Name at\n%s", m.funcNameOrigin) 340 | } 341 | 342 | if !m.NameMock.invocationsDone() && afterNameCounter > 0 { 343 | m.t.Errorf("Expected %d calls to GenericInMock.Name at\n%s but found %d calls", 344 | mm_atomic.LoadUint64(&m.NameMock.expectedInvocations), m.NameMock.expectedInvocationsOrigin, afterNameCounter) 345 | } 346 | } 347 | 348 | // MinimockFinish checks that all mocked methods have been called the expected number of times 349 | func (m *GenericInMock[T]) MinimockFinish() { 350 | m.finishOnce.Do(func() { 351 | if !m.minimockDone() { 352 | m.MinimockNameInspect() 353 | } 354 | }) 355 | } 356 | 357 | // MinimockWait waits for all mocked methods to be called the expected number of times 358 | func (m *GenericInMock[T]) MinimockWait(timeout mm_time.Duration) { 359 | timeoutCh := mm_time.After(timeout) 360 | for { 361 | if m.minimockDone() { 362 | return 363 | } 364 | select { 365 | case <-timeoutCh: 366 | m.MinimockFinish() 367 | return 368 | case <-mm_time.After(10 * mm_time.Millisecond): 369 | } 370 | } 371 | } 372 | 373 | func (m *GenericInMock[T]) minimockDone() bool { 374 | done := true 375 | return done && 376 | m.MinimockNameDone() 377 | } 378 | -------------------------------------------------------------------------------- /tests/reader_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate go run github.com/gojuno/minimock/v3/cmd/minimock -i github.com/gojuno/minimock/v3/tests.reader -o reader_mock.go -n ReaderMock -p tests -gr 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | ) 14 | 15 | // ReaderMock implements reader 16 | type ReaderMock struct { 17 | t minimock.Tester 18 | finishOnce sync.Once 19 | 20 | funcRead func(p []byte) (n int, err error) 21 | funcReadOrigin string 22 | inspectFuncRead func(p []byte) 23 | afterReadCounter uint64 24 | beforeReadCounter uint64 25 | ReadMock mReaderMockRead 26 | } 27 | 28 | // NewReaderMock returns a mock for reader 29 | func NewReaderMock(t minimock.Tester) *ReaderMock { 30 | m := &ReaderMock{t: t} 31 | 32 | if controller, ok := t.(minimock.MockController); ok { 33 | controller.RegisterMocker(m) 34 | } 35 | 36 | m.ReadMock = mReaderMockRead{mock: m} 37 | m.ReadMock.callArgs = []*ReaderMockReadParams{} 38 | 39 | t.Cleanup(m.MinimockFinish) 40 | 41 | return m 42 | } 43 | 44 | type mReaderMockRead struct { 45 | optional bool 46 | mock *ReaderMock 47 | defaultExpectation *ReaderMockReadExpectation 48 | expectations []*ReaderMockReadExpectation 49 | 50 | callArgs []*ReaderMockReadParams 51 | mutex sync.RWMutex 52 | 53 | expectedInvocations uint64 54 | expectedInvocationsOrigin string 55 | } 56 | 57 | // ReaderMockReadExpectation specifies expectation struct of the reader.Read 58 | type ReaderMockReadExpectation struct { 59 | mock *ReaderMock 60 | params *ReaderMockReadParams 61 | paramPtrs *ReaderMockReadParamPtrs 62 | expectationOrigins ReaderMockReadExpectationOrigins 63 | results *ReaderMockReadResults 64 | returnOrigin string 65 | Counter uint64 66 | } 67 | 68 | // ReaderMockReadParams contains parameters of the reader.Read 69 | type ReaderMockReadParams struct { 70 | p []byte 71 | } 72 | 73 | // ReaderMockReadParamPtrs contains pointers to parameters of the reader.Read 74 | type ReaderMockReadParamPtrs struct { 75 | p *[]byte 76 | } 77 | 78 | // ReaderMockReadResults contains results of the reader.Read 79 | type ReaderMockReadResults struct { 80 | n int 81 | err error 82 | } 83 | 84 | // ReaderMockReadOrigins contains origins of expectations of the reader.Read 85 | type ReaderMockReadExpectationOrigins struct { 86 | origin string 87 | originP string 88 | } 89 | 90 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 91 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 92 | // Optional() makes method check to work in '0 or more' mode. 93 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 94 | // catch the problems when the expected method call is totally skipped during test run. 95 | func (mmRead *mReaderMockRead) Optional() *mReaderMockRead { 96 | mmRead.optional = true 97 | return mmRead 98 | } 99 | 100 | // Expect sets up expected params for reader.Read 101 | func (mmRead *mReaderMockRead) Expect(p []byte) *mReaderMockRead { 102 | if mmRead.mock.funcRead != nil { 103 | mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") 104 | } 105 | 106 | if mmRead.defaultExpectation == nil { 107 | mmRead.defaultExpectation = &ReaderMockReadExpectation{} 108 | } 109 | 110 | if mmRead.defaultExpectation.paramPtrs != nil { 111 | mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by ExpectParams functions") 112 | } 113 | 114 | mmRead.defaultExpectation.params = &ReaderMockReadParams{p} 115 | mmRead.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) 116 | for _, e := range mmRead.expectations { 117 | if minimock.Equal(e.params, mmRead.defaultExpectation.params) { 118 | mmRead.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmRead.defaultExpectation.params) 119 | } 120 | } 121 | 122 | return mmRead 123 | } 124 | 125 | // ExpectPParam1 sets up expected param p for reader.Read 126 | func (mmRead *mReaderMockRead) ExpectPParam1(p []byte) *mReaderMockRead { 127 | if mmRead.mock.funcRead != nil { 128 | mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") 129 | } 130 | 131 | if mmRead.defaultExpectation == nil { 132 | mmRead.defaultExpectation = &ReaderMockReadExpectation{} 133 | } 134 | 135 | if mmRead.defaultExpectation.params != nil { 136 | mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Expect") 137 | } 138 | 139 | if mmRead.defaultExpectation.paramPtrs == nil { 140 | mmRead.defaultExpectation.paramPtrs = &ReaderMockReadParamPtrs{} 141 | } 142 | mmRead.defaultExpectation.paramPtrs.p = &p 143 | mmRead.defaultExpectation.expectationOrigins.originP = minimock.CallerInfo(1) 144 | 145 | return mmRead 146 | } 147 | 148 | // Inspect accepts an inspector function that has same arguments as the reader.Read 149 | func (mmRead *mReaderMockRead) Inspect(f func(p []byte)) *mReaderMockRead { 150 | if mmRead.mock.inspectFuncRead != nil { 151 | mmRead.mock.t.Fatalf("Inspect function is already set for ReaderMock.Read") 152 | } 153 | 154 | mmRead.mock.inspectFuncRead = f 155 | 156 | return mmRead 157 | } 158 | 159 | // Return sets up results that will be returned by reader.Read 160 | func (mmRead *mReaderMockRead) Return(n int, err error) *ReaderMock { 161 | if mmRead.mock.funcRead != nil { 162 | mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") 163 | } 164 | 165 | if mmRead.defaultExpectation == nil { 166 | mmRead.defaultExpectation = &ReaderMockReadExpectation{mock: mmRead.mock} 167 | } 168 | mmRead.defaultExpectation.results = &ReaderMockReadResults{n, err} 169 | mmRead.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 170 | return mmRead.mock 171 | } 172 | 173 | // Set uses given function f to mock the reader.Read method 174 | func (mmRead *mReaderMockRead) Set(f func(p []byte) (n int, err error)) *ReaderMock { 175 | if mmRead.defaultExpectation != nil { 176 | mmRead.mock.t.Fatalf("Default expectation is already set for the reader.Read method") 177 | } 178 | 179 | if len(mmRead.expectations) > 0 { 180 | mmRead.mock.t.Fatalf("Some expectations are already set for the reader.Read method") 181 | } 182 | 183 | mmRead.mock.funcRead = f 184 | mmRead.mock.funcReadOrigin = minimock.CallerInfo(1) 185 | return mmRead.mock 186 | } 187 | 188 | // When sets expectation for the reader.Read which will trigger the result defined by the following 189 | // Then helper 190 | func (mmRead *mReaderMockRead) When(p []byte) *ReaderMockReadExpectation { 191 | if mmRead.mock.funcRead != nil { 192 | mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") 193 | } 194 | 195 | expectation := &ReaderMockReadExpectation{ 196 | mock: mmRead.mock, 197 | params: &ReaderMockReadParams{p}, 198 | expectationOrigins: ReaderMockReadExpectationOrigins{origin: minimock.CallerInfo(1)}, 199 | } 200 | mmRead.expectations = append(mmRead.expectations, expectation) 201 | return expectation 202 | } 203 | 204 | // Then sets up reader.Read return parameters for the expectation previously defined by the When method 205 | func (e *ReaderMockReadExpectation) Then(n int, err error) *ReaderMock { 206 | e.results = &ReaderMockReadResults{n, err} 207 | return e.mock 208 | } 209 | 210 | // Times sets number of times reader.Read should be invoked 211 | func (mmRead *mReaderMockRead) Times(n uint64) *mReaderMockRead { 212 | if n == 0 { 213 | mmRead.mock.t.Fatalf("Times of ReaderMock.Read mock can not be zero") 214 | } 215 | mm_atomic.StoreUint64(&mmRead.expectedInvocations, n) 216 | mmRead.expectedInvocationsOrigin = minimock.CallerInfo(1) 217 | return mmRead 218 | } 219 | 220 | func (mmRead *mReaderMockRead) invocationsDone() bool { 221 | if len(mmRead.expectations) == 0 && mmRead.defaultExpectation == nil && mmRead.mock.funcRead == nil { 222 | return true 223 | } 224 | 225 | totalInvocations := mm_atomic.LoadUint64(&mmRead.mock.afterReadCounter) 226 | expectedInvocations := mm_atomic.LoadUint64(&mmRead.expectedInvocations) 227 | 228 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 229 | } 230 | 231 | // Read implements reader 232 | func (mmRead *ReaderMock) Read(p []byte) (n int, err error) { 233 | mm_atomic.AddUint64(&mmRead.beforeReadCounter, 1) 234 | defer mm_atomic.AddUint64(&mmRead.afterReadCounter, 1) 235 | 236 | mmRead.t.Helper() 237 | 238 | if mmRead.inspectFuncRead != nil { 239 | mmRead.inspectFuncRead(p) 240 | } 241 | 242 | mm_params := ReaderMockReadParams{p} 243 | 244 | // Record call args 245 | mmRead.ReadMock.mutex.Lock() 246 | mmRead.ReadMock.callArgs = append(mmRead.ReadMock.callArgs, &mm_params) 247 | mmRead.ReadMock.mutex.Unlock() 248 | 249 | for _, e := range mmRead.ReadMock.expectations { 250 | if minimock.Equal(*e.params, mm_params) { 251 | mm_atomic.AddUint64(&e.Counter, 1) 252 | return e.results.n, e.results.err 253 | } 254 | } 255 | 256 | if mmRead.ReadMock.defaultExpectation != nil { 257 | mm_atomic.AddUint64(&mmRead.ReadMock.defaultExpectation.Counter, 1) 258 | mm_want := mmRead.ReadMock.defaultExpectation.params 259 | mm_want_ptrs := mmRead.ReadMock.defaultExpectation.paramPtrs 260 | 261 | mm_got := ReaderMockReadParams{p} 262 | 263 | if mm_want_ptrs != nil { 264 | 265 | if mm_want_ptrs.p != nil && !minimock.Equal(*mm_want_ptrs.p, mm_got.p) { 266 | mmRead.t.Errorf("ReaderMock.Read got unexpected parameter p, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 267 | mmRead.ReadMock.defaultExpectation.expectationOrigins.originP, *mm_want_ptrs.p, mm_got.p, minimock.Diff(*mm_want_ptrs.p, mm_got.p)) 268 | } 269 | 270 | } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { 271 | mmRead.t.Errorf("ReaderMock.Read got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 272 | mmRead.ReadMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) 273 | } 274 | 275 | mm_results := mmRead.ReadMock.defaultExpectation.results 276 | if mm_results == nil { 277 | mmRead.t.Fatal("No results are set for the ReaderMock.Read") 278 | } 279 | return (*mm_results).n, (*mm_results).err 280 | } 281 | if mmRead.funcRead != nil { 282 | return mmRead.funcRead(p) 283 | } 284 | mmRead.t.Fatalf("Unexpected call to ReaderMock.Read. %v", p) 285 | return 286 | } 287 | 288 | // ReadAfterCounter returns a count of finished ReaderMock.Read invocations 289 | func (mmRead *ReaderMock) ReadAfterCounter() uint64 { 290 | return mm_atomic.LoadUint64(&mmRead.afterReadCounter) 291 | } 292 | 293 | // ReadBeforeCounter returns a count of ReaderMock.Read invocations 294 | func (mmRead *ReaderMock) ReadBeforeCounter() uint64 { 295 | return mm_atomic.LoadUint64(&mmRead.beforeReadCounter) 296 | } 297 | 298 | // Calls returns a list of arguments used in each call to ReaderMock.Read. 299 | // The list is in the same order as the calls were made (i.e. recent calls have a higher index) 300 | func (mmRead *mReaderMockRead) Calls() []*ReaderMockReadParams { 301 | mmRead.mutex.RLock() 302 | 303 | argCopy := make([]*ReaderMockReadParams, len(mmRead.callArgs)) 304 | copy(argCopy, mmRead.callArgs) 305 | 306 | mmRead.mutex.RUnlock() 307 | 308 | return argCopy 309 | } 310 | 311 | // MinimockReadDone returns true if the count of the Read invocations corresponds 312 | // the number of defined expectations 313 | func (m *ReaderMock) MinimockReadDone() bool { 314 | if m.ReadMock.optional { 315 | // Optional methods provide '0 or more' call count restriction. 316 | return true 317 | } 318 | 319 | for _, e := range m.ReadMock.expectations { 320 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 321 | return false 322 | } 323 | } 324 | 325 | return m.ReadMock.invocationsDone() 326 | } 327 | 328 | // MinimockReadInspect logs each unmet expectation 329 | func (m *ReaderMock) MinimockReadInspect() { 330 | for _, e := range m.ReadMock.expectations { 331 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 332 | m.t.Errorf("Expected call to ReaderMock.Read at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) 333 | } 334 | } 335 | 336 | afterReadCounter := mm_atomic.LoadUint64(&m.afterReadCounter) 337 | // if default expectation was set then invocations count should be greater than zero 338 | if m.ReadMock.defaultExpectation != nil && afterReadCounter < 1 { 339 | if m.ReadMock.defaultExpectation.params == nil { 340 | m.t.Errorf("Expected call to ReaderMock.Read at\n%s", m.ReadMock.defaultExpectation.returnOrigin) 341 | } else { 342 | m.t.Errorf("Expected call to ReaderMock.Read at\n%s with params: %#v", m.ReadMock.defaultExpectation.expectationOrigins.origin, *m.ReadMock.defaultExpectation.params) 343 | } 344 | } 345 | // if func was set then invocations count should be greater than zero 346 | if m.funcRead != nil && afterReadCounter < 1 { 347 | m.t.Errorf("Expected call to ReaderMock.Read at\n%s", m.funcReadOrigin) 348 | } 349 | 350 | if !m.ReadMock.invocationsDone() && afterReadCounter > 0 { 351 | m.t.Errorf("Expected %d calls to ReaderMock.Read at\n%s but found %d calls", 352 | mm_atomic.LoadUint64(&m.ReadMock.expectedInvocations), m.ReadMock.expectedInvocationsOrigin, afterReadCounter) 353 | } 354 | } 355 | 356 | // MinimockFinish checks that all mocked methods have been called the expected number of times 357 | func (m *ReaderMock) MinimockFinish() { 358 | m.finishOnce.Do(func() { 359 | if !m.minimockDone() { 360 | m.MinimockReadInspect() 361 | } 362 | }) 363 | } 364 | 365 | // MinimockWait waits for all mocked methods to be called the expected number of times 366 | func (m *ReaderMock) MinimockWait(timeout mm_time.Duration) { 367 | timeoutCh := mm_time.After(timeout) 368 | for { 369 | if m.minimockDone() { 370 | return 371 | } 372 | select { 373 | case <-timeoutCh: 374 | m.MinimockFinish() 375 | return 376 | case <-mm_time.After(10 * mm_time.Millisecond): 377 | } 378 | } 379 | } 380 | 381 | func (m *ReaderMock) minimockDone() bool { 382 | done := true 383 | return done && 384 | m.MinimockReadDone() 385 | } 386 | -------------------------------------------------------------------------------- /tests/generic_specific.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate minimock -i github.com/gojuno/minimock/v3/tests.genericSpecific -o generic_specific.go -n GenericSpecificMock -p tests 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | "google.golang.org/protobuf/proto" 14 | ) 15 | 16 | // GenericSpecificMock implements genericSpecific 17 | type GenericSpecificMock[T proto.Message] struct { 18 | t minimock.Tester 19 | finishOnce sync.Once 20 | 21 | funcName func(t1 T) 22 | funcNameOrigin string 23 | inspectFuncName func(t1 T) 24 | afterNameCounter uint64 25 | beforeNameCounter uint64 26 | NameMock mGenericSpecificMockName[T] 27 | } 28 | 29 | // NewGenericSpecificMock returns a mock for genericSpecific 30 | func NewGenericSpecificMock[T proto.Message](t minimock.Tester) *GenericSpecificMock[T] { 31 | m := &GenericSpecificMock[T]{t: t} 32 | 33 | if controller, ok := t.(minimock.MockController); ok { 34 | controller.RegisterMocker(m) 35 | } 36 | 37 | m.NameMock = mGenericSpecificMockName[T]{mock: m} 38 | m.NameMock.callArgs = []*GenericSpecificMockNameParams[T]{} 39 | 40 | t.Cleanup(m.MinimockFinish) 41 | 42 | return m 43 | } 44 | 45 | type mGenericSpecificMockName[T proto.Message] struct { 46 | optional bool 47 | mock *GenericSpecificMock[T] 48 | defaultExpectation *GenericSpecificMockNameExpectation[T] 49 | expectations []*GenericSpecificMockNameExpectation[T] 50 | 51 | callArgs []*GenericSpecificMockNameParams[T] 52 | mutex sync.RWMutex 53 | 54 | expectedInvocations uint64 55 | expectedInvocationsOrigin string 56 | } 57 | 58 | // GenericSpecificMockNameExpectation specifies expectation struct of the genericSpecific.Name 59 | type GenericSpecificMockNameExpectation[T proto.Message] struct { 60 | mock *GenericSpecificMock[T] 61 | params *GenericSpecificMockNameParams[T] 62 | paramPtrs *GenericSpecificMockNameParamPtrs[T] 63 | expectationOrigins GenericSpecificMockNameExpectationOrigins 64 | 65 | returnOrigin string 66 | Counter uint64 67 | } 68 | 69 | // GenericSpecificMockNameParams contains parameters of the genericSpecific.Name 70 | type GenericSpecificMockNameParams[T proto.Message] struct { 71 | t1 T 72 | } 73 | 74 | // GenericSpecificMockNameParamPtrs contains pointers to parameters of the genericSpecific.Name 75 | type GenericSpecificMockNameParamPtrs[T proto.Message] struct { 76 | t1 *T 77 | } 78 | 79 | // GenericSpecificMockNameOrigins contains origins of expectations of the genericSpecific.Name 80 | type GenericSpecificMockNameExpectationOrigins struct { 81 | origin string 82 | originT1 string 83 | } 84 | 85 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 86 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 87 | // Optional() makes method check to work in '0 or more' mode. 88 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 89 | // catch the problems when the expected method call is totally skipped during test run. 90 | func (mmName *mGenericSpecificMockName[T]) Optional() *mGenericSpecificMockName[T] { 91 | mmName.optional = true 92 | return mmName 93 | } 94 | 95 | // Expect sets up expected params for genericSpecific.Name 96 | func (mmName *mGenericSpecificMockName[T]) Expect(t1 T) *mGenericSpecificMockName[T] { 97 | if mmName.mock.funcName != nil { 98 | mmName.mock.t.Fatalf("GenericSpecificMock.Name mock is already set by Set") 99 | } 100 | 101 | if mmName.defaultExpectation == nil { 102 | mmName.defaultExpectation = &GenericSpecificMockNameExpectation[T]{} 103 | } 104 | 105 | if mmName.defaultExpectation.paramPtrs != nil { 106 | mmName.mock.t.Fatalf("GenericSpecificMock.Name mock is already set by ExpectParams functions") 107 | } 108 | 109 | mmName.defaultExpectation.params = &GenericSpecificMockNameParams[T]{t1} 110 | mmName.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) 111 | for _, e := range mmName.expectations { 112 | if minimock.Equal(e.params, mmName.defaultExpectation.params) { 113 | mmName.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmName.defaultExpectation.params) 114 | } 115 | } 116 | 117 | return mmName 118 | } 119 | 120 | // ExpectT1Param1 sets up expected param t1 for genericSpecific.Name 121 | func (mmName *mGenericSpecificMockName[T]) ExpectT1Param1(t1 T) *mGenericSpecificMockName[T] { 122 | if mmName.mock.funcName != nil { 123 | mmName.mock.t.Fatalf("GenericSpecificMock.Name mock is already set by Set") 124 | } 125 | 126 | if mmName.defaultExpectation == nil { 127 | mmName.defaultExpectation = &GenericSpecificMockNameExpectation[T]{} 128 | } 129 | 130 | if mmName.defaultExpectation.params != nil { 131 | mmName.mock.t.Fatalf("GenericSpecificMock.Name mock is already set by Expect") 132 | } 133 | 134 | if mmName.defaultExpectation.paramPtrs == nil { 135 | mmName.defaultExpectation.paramPtrs = &GenericSpecificMockNameParamPtrs[T]{} 136 | } 137 | mmName.defaultExpectation.paramPtrs.t1 = &t1 138 | mmName.defaultExpectation.expectationOrigins.originT1 = minimock.CallerInfo(1) 139 | 140 | return mmName 141 | } 142 | 143 | // Inspect accepts an inspector function that has same arguments as the genericSpecific.Name 144 | func (mmName *mGenericSpecificMockName[T]) Inspect(f func(t1 T)) *mGenericSpecificMockName[T] { 145 | if mmName.mock.inspectFuncName != nil { 146 | mmName.mock.t.Fatalf("Inspect function is already set for GenericSpecificMock.Name") 147 | } 148 | 149 | mmName.mock.inspectFuncName = f 150 | 151 | return mmName 152 | } 153 | 154 | // Return sets up results that will be returned by genericSpecific.Name 155 | func (mmName *mGenericSpecificMockName[T]) Return() *GenericSpecificMock[T] { 156 | if mmName.mock.funcName != nil { 157 | mmName.mock.t.Fatalf("GenericSpecificMock.Name mock is already set by Set") 158 | } 159 | 160 | if mmName.defaultExpectation == nil { 161 | mmName.defaultExpectation = &GenericSpecificMockNameExpectation[T]{mock: mmName.mock} 162 | } 163 | 164 | mmName.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 165 | return mmName.mock 166 | } 167 | 168 | // Set uses given function f to mock the genericSpecific.Name method 169 | func (mmName *mGenericSpecificMockName[T]) Set(f func(t1 T)) *GenericSpecificMock[T] { 170 | if mmName.defaultExpectation != nil { 171 | mmName.mock.t.Fatalf("Default expectation is already set for the genericSpecific.Name method") 172 | } 173 | 174 | if len(mmName.expectations) > 0 { 175 | mmName.mock.t.Fatalf("Some expectations are already set for the genericSpecific.Name method") 176 | } 177 | 178 | mmName.mock.funcName = f 179 | mmName.mock.funcNameOrigin = minimock.CallerInfo(1) 180 | return mmName.mock 181 | } 182 | 183 | // When sets expectation for the genericSpecific.Name which will trigger the result defined by the following 184 | // Then helper 185 | func (mmName *mGenericSpecificMockName[T]) When(t1 T) *GenericSpecificMockNameExpectation[T] { 186 | if mmName.mock.funcName != nil { 187 | mmName.mock.t.Fatalf("GenericSpecificMock.Name mock is already set by Set") 188 | } 189 | 190 | expectation := &GenericSpecificMockNameExpectation[T]{ 191 | mock: mmName.mock, 192 | params: &GenericSpecificMockNameParams[T]{t1}, 193 | expectationOrigins: GenericSpecificMockNameExpectationOrigins{origin: minimock.CallerInfo(1)}, 194 | } 195 | mmName.expectations = append(mmName.expectations, expectation) 196 | return expectation 197 | } 198 | 199 | // Then sets up genericSpecific.Name return parameters for the expectation previously defined by the When method 200 | 201 | func (e *GenericSpecificMockNameExpectation[T]) Then() *GenericSpecificMock[T] { 202 | return e.mock 203 | } 204 | 205 | // Times sets number of times genericSpecific.Name should be invoked 206 | func (mmName *mGenericSpecificMockName[T]) Times(n uint64) *mGenericSpecificMockName[T] { 207 | if n == 0 { 208 | mmName.mock.t.Fatalf("Times of GenericSpecificMock.Name mock can not be zero") 209 | } 210 | mm_atomic.StoreUint64(&mmName.expectedInvocations, n) 211 | mmName.expectedInvocationsOrigin = minimock.CallerInfo(1) 212 | return mmName 213 | } 214 | 215 | func (mmName *mGenericSpecificMockName[T]) invocationsDone() bool { 216 | if len(mmName.expectations) == 0 && mmName.defaultExpectation == nil && mmName.mock.funcName == nil { 217 | return true 218 | } 219 | 220 | totalInvocations := mm_atomic.LoadUint64(&mmName.mock.afterNameCounter) 221 | expectedInvocations := mm_atomic.LoadUint64(&mmName.expectedInvocations) 222 | 223 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 224 | } 225 | 226 | // Name implements genericSpecific 227 | func (mmName *GenericSpecificMock[T]) Name(t1 T) { 228 | mm_atomic.AddUint64(&mmName.beforeNameCounter, 1) 229 | defer mm_atomic.AddUint64(&mmName.afterNameCounter, 1) 230 | 231 | mmName.t.Helper() 232 | 233 | if mmName.inspectFuncName != nil { 234 | mmName.inspectFuncName(t1) 235 | } 236 | 237 | mm_params := GenericSpecificMockNameParams[T]{t1} 238 | 239 | // Record call args 240 | mmName.NameMock.mutex.Lock() 241 | mmName.NameMock.callArgs = append(mmName.NameMock.callArgs, &mm_params) 242 | mmName.NameMock.mutex.Unlock() 243 | 244 | for _, e := range mmName.NameMock.expectations { 245 | if minimock.Equal(*e.params, mm_params) { 246 | mm_atomic.AddUint64(&e.Counter, 1) 247 | return 248 | } 249 | } 250 | 251 | if mmName.NameMock.defaultExpectation != nil { 252 | mm_atomic.AddUint64(&mmName.NameMock.defaultExpectation.Counter, 1) 253 | mm_want := mmName.NameMock.defaultExpectation.params 254 | mm_want_ptrs := mmName.NameMock.defaultExpectation.paramPtrs 255 | 256 | mm_got := GenericSpecificMockNameParams[T]{t1} 257 | 258 | if mm_want_ptrs != nil { 259 | 260 | if mm_want_ptrs.t1 != nil && !minimock.Equal(*mm_want_ptrs.t1, mm_got.t1) { 261 | mmName.t.Errorf("GenericSpecificMock.Name got unexpected parameter t1, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 262 | mmName.NameMock.defaultExpectation.expectationOrigins.originT1, *mm_want_ptrs.t1, mm_got.t1, minimock.Diff(*mm_want_ptrs.t1, mm_got.t1)) 263 | } 264 | 265 | } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { 266 | mmName.t.Errorf("GenericSpecificMock.Name got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 267 | mmName.NameMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) 268 | } 269 | 270 | return 271 | 272 | } 273 | if mmName.funcName != nil { 274 | mmName.funcName(t1) 275 | return 276 | } 277 | mmName.t.Fatalf("Unexpected call to GenericSpecificMock.Name. %v", t1) 278 | 279 | } 280 | 281 | // NameAfterCounter returns a count of finished GenericSpecificMock.Name invocations 282 | func (mmName *GenericSpecificMock[T]) NameAfterCounter() uint64 { 283 | return mm_atomic.LoadUint64(&mmName.afterNameCounter) 284 | } 285 | 286 | // NameBeforeCounter returns a count of GenericSpecificMock.Name invocations 287 | func (mmName *GenericSpecificMock[T]) NameBeforeCounter() uint64 { 288 | return mm_atomic.LoadUint64(&mmName.beforeNameCounter) 289 | } 290 | 291 | // Calls returns a list of arguments used in each call to GenericSpecificMock.Name. 292 | // The list is in the same order as the calls were made (i.e. recent calls have a higher index) 293 | func (mmName *mGenericSpecificMockName[T]) Calls() []*GenericSpecificMockNameParams[T] { 294 | mmName.mutex.RLock() 295 | 296 | argCopy := make([]*GenericSpecificMockNameParams[T], len(mmName.callArgs)) 297 | copy(argCopy, mmName.callArgs) 298 | 299 | mmName.mutex.RUnlock() 300 | 301 | return argCopy 302 | } 303 | 304 | // MinimockNameDone returns true if the count of the Name invocations corresponds 305 | // the number of defined expectations 306 | func (m *GenericSpecificMock[T]) MinimockNameDone() bool { 307 | if m.NameMock.optional { 308 | // Optional methods provide '0 or more' call count restriction. 309 | return true 310 | } 311 | 312 | for _, e := range m.NameMock.expectations { 313 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 314 | return false 315 | } 316 | } 317 | 318 | return m.NameMock.invocationsDone() 319 | } 320 | 321 | // MinimockNameInspect logs each unmet expectation 322 | func (m *GenericSpecificMock[T]) MinimockNameInspect() { 323 | for _, e := range m.NameMock.expectations { 324 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 325 | m.t.Errorf("Expected call to GenericSpecificMock.Name at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) 326 | } 327 | } 328 | 329 | afterNameCounter := mm_atomic.LoadUint64(&m.afterNameCounter) 330 | // if default expectation was set then invocations count should be greater than zero 331 | if m.NameMock.defaultExpectation != nil && afterNameCounter < 1 { 332 | if m.NameMock.defaultExpectation.params == nil { 333 | m.t.Errorf("Expected call to GenericSpecificMock.Name at\n%s", m.NameMock.defaultExpectation.returnOrigin) 334 | } else { 335 | m.t.Errorf("Expected call to GenericSpecificMock.Name at\n%s with params: %#v", m.NameMock.defaultExpectation.expectationOrigins.origin, *m.NameMock.defaultExpectation.params) 336 | } 337 | } 338 | // if func was set then invocations count should be greater than zero 339 | if m.funcName != nil && afterNameCounter < 1 { 340 | m.t.Errorf("Expected call to GenericSpecificMock.Name at\n%s", m.funcNameOrigin) 341 | } 342 | 343 | if !m.NameMock.invocationsDone() && afterNameCounter > 0 { 344 | m.t.Errorf("Expected %d calls to GenericSpecificMock.Name at\n%s but found %d calls", 345 | mm_atomic.LoadUint64(&m.NameMock.expectedInvocations), m.NameMock.expectedInvocationsOrigin, afterNameCounter) 346 | } 347 | } 348 | 349 | // MinimockFinish checks that all mocked methods have been called the expected number of times 350 | func (m *GenericSpecificMock[T]) MinimockFinish() { 351 | m.finishOnce.Do(func() { 352 | if !m.minimockDone() { 353 | m.MinimockNameInspect() 354 | } 355 | }) 356 | } 357 | 358 | // MinimockWait waits for all mocked methods to be called the expected number of times 359 | func (m *GenericSpecificMock[T]) MinimockWait(timeout mm_time.Duration) { 360 | timeoutCh := mm_time.After(timeout) 361 | for { 362 | if m.minimockDone() { 363 | return 364 | } 365 | select { 366 | case <-timeoutCh: 367 | m.MinimockFinish() 368 | return 369 | case <-mm_time.After(10 * mm_time.Millisecond): 370 | } 371 | } 372 | } 373 | 374 | func (m *GenericSpecificMock[T]) minimockDone() bool { 375 | done := true 376 | return done && 377 | m.MinimockNameDone() 378 | } 379 | -------------------------------------------------------------------------------- /tests/generic_inout.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate minimock -i github.com/gojuno/minimock/v3/tests.genericInout -o generic_inout.go -n GenericInoutMock -p tests 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | ) 14 | 15 | // GenericInoutMock implements genericInout 16 | type GenericInoutMock[T any] struct { 17 | t minimock.Tester 18 | finishOnce sync.Once 19 | 20 | funcName func(t1 T) (t2 T) 21 | funcNameOrigin string 22 | inspectFuncName func(t1 T) 23 | afterNameCounter uint64 24 | beforeNameCounter uint64 25 | NameMock mGenericInoutMockName[T] 26 | } 27 | 28 | // NewGenericInoutMock returns a mock for genericInout 29 | func NewGenericInoutMock[T any](t minimock.Tester) *GenericInoutMock[T] { 30 | m := &GenericInoutMock[T]{t: t} 31 | 32 | if controller, ok := t.(minimock.MockController); ok { 33 | controller.RegisterMocker(m) 34 | } 35 | 36 | m.NameMock = mGenericInoutMockName[T]{mock: m} 37 | m.NameMock.callArgs = []*GenericInoutMockNameParams[T]{} 38 | 39 | t.Cleanup(m.MinimockFinish) 40 | 41 | return m 42 | } 43 | 44 | type mGenericInoutMockName[T any] struct { 45 | optional bool 46 | mock *GenericInoutMock[T] 47 | defaultExpectation *GenericInoutMockNameExpectation[T] 48 | expectations []*GenericInoutMockNameExpectation[T] 49 | 50 | callArgs []*GenericInoutMockNameParams[T] 51 | mutex sync.RWMutex 52 | 53 | expectedInvocations uint64 54 | expectedInvocationsOrigin string 55 | } 56 | 57 | // GenericInoutMockNameExpectation specifies expectation struct of the genericInout.Name 58 | type GenericInoutMockNameExpectation[T any] struct { 59 | mock *GenericInoutMock[T] 60 | params *GenericInoutMockNameParams[T] 61 | paramPtrs *GenericInoutMockNameParamPtrs[T] 62 | expectationOrigins GenericInoutMockNameExpectationOrigins 63 | results *GenericInoutMockNameResults[T] 64 | returnOrigin string 65 | Counter uint64 66 | } 67 | 68 | // GenericInoutMockNameParams contains parameters of the genericInout.Name 69 | type GenericInoutMockNameParams[T any] struct { 70 | t1 T 71 | } 72 | 73 | // GenericInoutMockNameParamPtrs contains pointers to parameters of the genericInout.Name 74 | type GenericInoutMockNameParamPtrs[T any] struct { 75 | t1 *T 76 | } 77 | 78 | // GenericInoutMockNameResults contains results of the genericInout.Name 79 | type GenericInoutMockNameResults[T any] struct { 80 | t2 T 81 | } 82 | 83 | // GenericInoutMockNameOrigins contains origins of expectations of the genericInout.Name 84 | type GenericInoutMockNameExpectationOrigins struct { 85 | origin string 86 | originT1 string 87 | } 88 | 89 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 90 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 91 | // Optional() makes method check to work in '0 or more' mode. 92 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 93 | // catch the problems when the expected method call is totally skipped during test run. 94 | func (mmName *mGenericInoutMockName[T]) Optional() *mGenericInoutMockName[T] { 95 | mmName.optional = true 96 | return mmName 97 | } 98 | 99 | // Expect sets up expected params for genericInout.Name 100 | func (mmName *mGenericInoutMockName[T]) Expect(t1 T) *mGenericInoutMockName[T] { 101 | if mmName.mock.funcName != nil { 102 | mmName.mock.t.Fatalf("GenericInoutMock.Name mock is already set by Set") 103 | } 104 | 105 | if mmName.defaultExpectation == nil { 106 | mmName.defaultExpectation = &GenericInoutMockNameExpectation[T]{} 107 | } 108 | 109 | if mmName.defaultExpectation.paramPtrs != nil { 110 | mmName.mock.t.Fatalf("GenericInoutMock.Name mock is already set by ExpectParams functions") 111 | } 112 | 113 | mmName.defaultExpectation.params = &GenericInoutMockNameParams[T]{t1} 114 | mmName.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) 115 | for _, e := range mmName.expectations { 116 | if minimock.Equal(e.params, mmName.defaultExpectation.params) { 117 | mmName.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmName.defaultExpectation.params) 118 | } 119 | } 120 | 121 | return mmName 122 | } 123 | 124 | // ExpectT1Param1 sets up expected param t1 for genericInout.Name 125 | func (mmName *mGenericInoutMockName[T]) ExpectT1Param1(t1 T) *mGenericInoutMockName[T] { 126 | if mmName.mock.funcName != nil { 127 | mmName.mock.t.Fatalf("GenericInoutMock.Name mock is already set by Set") 128 | } 129 | 130 | if mmName.defaultExpectation == nil { 131 | mmName.defaultExpectation = &GenericInoutMockNameExpectation[T]{} 132 | } 133 | 134 | if mmName.defaultExpectation.params != nil { 135 | mmName.mock.t.Fatalf("GenericInoutMock.Name mock is already set by Expect") 136 | } 137 | 138 | if mmName.defaultExpectation.paramPtrs == nil { 139 | mmName.defaultExpectation.paramPtrs = &GenericInoutMockNameParamPtrs[T]{} 140 | } 141 | mmName.defaultExpectation.paramPtrs.t1 = &t1 142 | mmName.defaultExpectation.expectationOrigins.originT1 = minimock.CallerInfo(1) 143 | 144 | return mmName 145 | } 146 | 147 | // Inspect accepts an inspector function that has same arguments as the genericInout.Name 148 | func (mmName *mGenericInoutMockName[T]) Inspect(f func(t1 T)) *mGenericInoutMockName[T] { 149 | if mmName.mock.inspectFuncName != nil { 150 | mmName.mock.t.Fatalf("Inspect function is already set for GenericInoutMock.Name") 151 | } 152 | 153 | mmName.mock.inspectFuncName = f 154 | 155 | return mmName 156 | } 157 | 158 | // Return sets up results that will be returned by genericInout.Name 159 | func (mmName *mGenericInoutMockName[T]) Return(t2 T) *GenericInoutMock[T] { 160 | if mmName.mock.funcName != nil { 161 | mmName.mock.t.Fatalf("GenericInoutMock.Name mock is already set by Set") 162 | } 163 | 164 | if mmName.defaultExpectation == nil { 165 | mmName.defaultExpectation = &GenericInoutMockNameExpectation[T]{mock: mmName.mock} 166 | } 167 | mmName.defaultExpectation.results = &GenericInoutMockNameResults[T]{t2} 168 | mmName.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 169 | return mmName.mock 170 | } 171 | 172 | // Set uses given function f to mock the genericInout.Name method 173 | func (mmName *mGenericInoutMockName[T]) Set(f func(t1 T) (t2 T)) *GenericInoutMock[T] { 174 | if mmName.defaultExpectation != nil { 175 | mmName.mock.t.Fatalf("Default expectation is already set for the genericInout.Name method") 176 | } 177 | 178 | if len(mmName.expectations) > 0 { 179 | mmName.mock.t.Fatalf("Some expectations are already set for the genericInout.Name method") 180 | } 181 | 182 | mmName.mock.funcName = f 183 | mmName.mock.funcNameOrigin = minimock.CallerInfo(1) 184 | return mmName.mock 185 | } 186 | 187 | // When sets expectation for the genericInout.Name which will trigger the result defined by the following 188 | // Then helper 189 | func (mmName *mGenericInoutMockName[T]) When(t1 T) *GenericInoutMockNameExpectation[T] { 190 | if mmName.mock.funcName != nil { 191 | mmName.mock.t.Fatalf("GenericInoutMock.Name mock is already set by Set") 192 | } 193 | 194 | expectation := &GenericInoutMockNameExpectation[T]{ 195 | mock: mmName.mock, 196 | params: &GenericInoutMockNameParams[T]{t1}, 197 | expectationOrigins: GenericInoutMockNameExpectationOrigins{origin: minimock.CallerInfo(1)}, 198 | } 199 | mmName.expectations = append(mmName.expectations, expectation) 200 | return expectation 201 | } 202 | 203 | // Then sets up genericInout.Name return parameters for the expectation previously defined by the When method 204 | func (e *GenericInoutMockNameExpectation[T]) Then(t2 T) *GenericInoutMock[T] { 205 | e.results = &GenericInoutMockNameResults[T]{t2} 206 | return e.mock 207 | } 208 | 209 | // Times sets number of times genericInout.Name should be invoked 210 | func (mmName *mGenericInoutMockName[T]) Times(n uint64) *mGenericInoutMockName[T] { 211 | if n == 0 { 212 | mmName.mock.t.Fatalf("Times of GenericInoutMock.Name mock can not be zero") 213 | } 214 | mm_atomic.StoreUint64(&mmName.expectedInvocations, n) 215 | mmName.expectedInvocationsOrigin = minimock.CallerInfo(1) 216 | return mmName 217 | } 218 | 219 | func (mmName *mGenericInoutMockName[T]) invocationsDone() bool { 220 | if len(mmName.expectations) == 0 && mmName.defaultExpectation == nil && mmName.mock.funcName == nil { 221 | return true 222 | } 223 | 224 | totalInvocations := mm_atomic.LoadUint64(&mmName.mock.afterNameCounter) 225 | expectedInvocations := mm_atomic.LoadUint64(&mmName.expectedInvocations) 226 | 227 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 228 | } 229 | 230 | // Name implements genericInout 231 | func (mmName *GenericInoutMock[T]) Name(t1 T) (t2 T) { 232 | mm_atomic.AddUint64(&mmName.beforeNameCounter, 1) 233 | defer mm_atomic.AddUint64(&mmName.afterNameCounter, 1) 234 | 235 | mmName.t.Helper() 236 | 237 | if mmName.inspectFuncName != nil { 238 | mmName.inspectFuncName(t1) 239 | } 240 | 241 | mm_params := GenericInoutMockNameParams[T]{t1} 242 | 243 | // Record call args 244 | mmName.NameMock.mutex.Lock() 245 | mmName.NameMock.callArgs = append(mmName.NameMock.callArgs, &mm_params) 246 | mmName.NameMock.mutex.Unlock() 247 | 248 | for _, e := range mmName.NameMock.expectations { 249 | if minimock.Equal(*e.params, mm_params) { 250 | mm_atomic.AddUint64(&e.Counter, 1) 251 | return e.results.t2 252 | } 253 | } 254 | 255 | if mmName.NameMock.defaultExpectation != nil { 256 | mm_atomic.AddUint64(&mmName.NameMock.defaultExpectation.Counter, 1) 257 | mm_want := mmName.NameMock.defaultExpectation.params 258 | mm_want_ptrs := mmName.NameMock.defaultExpectation.paramPtrs 259 | 260 | mm_got := GenericInoutMockNameParams[T]{t1} 261 | 262 | if mm_want_ptrs != nil { 263 | 264 | if mm_want_ptrs.t1 != nil && !minimock.Equal(*mm_want_ptrs.t1, mm_got.t1) { 265 | mmName.t.Errorf("GenericInoutMock.Name got unexpected parameter t1, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 266 | mmName.NameMock.defaultExpectation.expectationOrigins.originT1, *mm_want_ptrs.t1, mm_got.t1, minimock.Diff(*mm_want_ptrs.t1, mm_got.t1)) 267 | } 268 | 269 | } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { 270 | mmName.t.Errorf("GenericInoutMock.Name got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 271 | mmName.NameMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) 272 | } 273 | 274 | mm_results := mmName.NameMock.defaultExpectation.results 275 | if mm_results == nil { 276 | mmName.t.Fatal("No results are set for the GenericInoutMock.Name") 277 | } 278 | return (*mm_results).t2 279 | } 280 | if mmName.funcName != nil { 281 | return mmName.funcName(t1) 282 | } 283 | mmName.t.Fatalf("Unexpected call to GenericInoutMock.Name. %v", t1) 284 | return 285 | } 286 | 287 | // NameAfterCounter returns a count of finished GenericInoutMock.Name invocations 288 | func (mmName *GenericInoutMock[T]) NameAfterCounter() uint64 { 289 | return mm_atomic.LoadUint64(&mmName.afterNameCounter) 290 | } 291 | 292 | // NameBeforeCounter returns a count of GenericInoutMock.Name invocations 293 | func (mmName *GenericInoutMock[T]) NameBeforeCounter() uint64 { 294 | return mm_atomic.LoadUint64(&mmName.beforeNameCounter) 295 | } 296 | 297 | // Calls returns a list of arguments used in each call to GenericInoutMock.Name. 298 | // The list is in the same order as the calls were made (i.e. recent calls have a higher index) 299 | func (mmName *mGenericInoutMockName[T]) Calls() []*GenericInoutMockNameParams[T] { 300 | mmName.mutex.RLock() 301 | 302 | argCopy := make([]*GenericInoutMockNameParams[T], len(mmName.callArgs)) 303 | copy(argCopy, mmName.callArgs) 304 | 305 | mmName.mutex.RUnlock() 306 | 307 | return argCopy 308 | } 309 | 310 | // MinimockNameDone returns true if the count of the Name invocations corresponds 311 | // the number of defined expectations 312 | func (m *GenericInoutMock[T]) MinimockNameDone() bool { 313 | if m.NameMock.optional { 314 | // Optional methods provide '0 or more' call count restriction. 315 | return true 316 | } 317 | 318 | for _, e := range m.NameMock.expectations { 319 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 320 | return false 321 | } 322 | } 323 | 324 | return m.NameMock.invocationsDone() 325 | } 326 | 327 | // MinimockNameInspect logs each unmet expectation 328 | func (m *GenericInoutMock[T]) MinimockNameInspect() { 329 | for _, e := range m.NameMock.expectations { 330 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 331 | m.t.Errorf("Expected call to GenericInoutMock.Name at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) 332 | } 333 | } 334 | 335 | afterNameCounter := mm_atomic.LoadUint64(&m.afterNameCounter) 336 | // if default expectation was set then invocations count should be greater than zero 337 | if m.NameMock.defaultExpectation != nil && afterNameCounter < 1 { 338 | if m.NameMock.defaultExpectation.params == nil { 339 | m.t.Errorf("Expected call to GenericInoutMock.Name at\n%s", m.NameMock.defaultExpectation.returnOrigin) 340 | } else { 341 | m.t.Errorf("Expected call to GenericInoutMock.Name at\n%s with params: %#v", m.NameMock.defaultExpectation.expectationOrigins.origin, *m.NameMock.defaultExpectation.params) 342 | } 343 | } 344 | // if func was set then invocations count should be greater than zero 345 | if m.funcName != nil && afterNameCounter < 1 { 346 | m.t.Errorf("Expected call to GenericInoutMock.Name at\n%s", m.funcNameOrigin) 347 | } 348 | 349 | if !m.NameMock.invocationsDone() && afterNameCounter > 0 { 350 | m.t.Errorf("Expected %d calls to GenericInoutMock.Name at\n%s but found %d calls", 351 | mm_atomic.LoadUint64(&m.NameMock.expectedInvocations), m.NameMock.expectedInvocationsOrigin, afterNameCounter) 352 | } 353 | } 354 | 355 | // MinimockFinish checks that all mocked methods have been called the expected number of times 356 | func (m *GenericInoutMock[T]) MinimockFinish() { 357 | m.finishOnce.Do(func() { 358 | if !m.minimockDone() { 359 | m.MinimockNameInspect() 360 | } 361 | }) 362 | } 363 | 364 | // MinimockWait waits for all mocked methods to be called the expected number of times 365 | func (m *GenericInoutMock[T]) MinimockWait(timeout mm_time.Duration) { 366 | timeoutCh := mm_time.After(timeout) 367 | for { 368 | if m.minimockDone() { 369 | return 370 | } 371 | select { 372 | case <-timeoutCh: 373 | m.MinimockFinish() 374 | return 375 | case <-mm_time.After(10 * mm_time.Millisecond): 376 | } 377 | } 378 | } 379 | 380 | func (m *GenericInoutMock[T]) minimockDone() bool { 381 | done := true 382 | return done && 383 | m.MinimockNameDone() 384 | } 385 | -------------------------------------------------------------------------------- /tests/generic_simple_union.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate minimock -i github.com/gojuno/minimock/v3/tests.genericSimpleUnion -o generic_simple_union.go -n GenericSimpleUnionMock -p tests 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | ) 14 | 15 | // GenericSimpleUnionMock implements genericSimpleUnion 16 | type GenericSimpleUnionMock[T simpleUnion] struct { 17 | t minimock.Tester 18 | finishOnce sync.Once 19 | 20 | funcName func(t1 T) 21 | funcNameOrigin string 22 | inspectFuncName func(t1 T) 23 | afterNameCounter uint64 24 | beforeNameCounter uint64 25 | NameMock mGenericSimpleUnionMockName[T] 26 | } 27 | 28 | // NewGenericSimpleUnionMock returns a mock for genericSimpleUnion 29 | func NewGenericSimpleUnionMock[T simpleUnion](t minimock.Tester) *GenericSimpleUnionMock[T] { 30 | m := &GenericSimpleUnionMock[T]{t: t} 31 | 32 | if controller, ok := t.(minimock.MockController); ok { 33 | controller.RegisterMocker(m) 34 | } 35 | 36 | m.NameMock = mGenericSimpleUnionMockName[T]{mock: m} 37 | m.NameMock.callArgs = []*GenericSimpleUnionMockNameParams[T]{} 38 | 39 | t.Cleanup(m.MinimockFinish) 40 | 41 | return m 42 | } 43 | 44 | type mGenericSimpleUnionMockName[T simpleUnion] struct { 45 | optional bool 46 | mock *GenericSimpleUnionMock[T] 47 | defaultExpectation *GenericSimpleUnionMockNameExpectation[T] 48 | expectations []*GenericSimpleUnionMockNameExpectation[T] 49 | 50 | callArgs []*GenericSimpleUnionMockNameParams[T] 51 | mutex sync.RWMutex 52 | 53 | expectedInvocations uint64 54 | expectedInvocationsOrigin string 55 | } 56 | 57 | // GenericSimpleUnionMockNameExpectation specifies expectation struct of the genericSimpleUnion.Name 58 | type GenericSimpleUnionMockNameExpectation[T simpleUnion] struct { 59 | mock *GenericSimpleUnionMock[T] 60 | params *GenericSimpleUnionMockNameParams[T] 61 | paramPtrs *GenericSimpleUnionMockNameParamPtrs[T] 62 | expectationOrigins GenericSimpleUnionMockNameExpectationOrigins 63 | 64 | returnOrigin string 65 | Counter uint64 66 | } 67 | 68 | // GenericSimpleUnionMockNameParams contains parameters of the genericSimpleUnion.Name 69 | type GenericSimpleUnionMockNameParams[T simpleUnion] struct { 70 | t1 T 71 | } 72 | 73 | // GenericSimpleUnionMockNameParamPtrs contains pointers to parameters of the genericSimpleUnion.Name 74 | type GenericSimpleUnionMockNameParamPtrs[T simpleUnion] struct { 75 | t1 *T 76 | } 77 | 78 | // GenericSimpleUnionMockNameOrigins contains origins of expectations of the genericSimpleUnion.Name 79 | type GenericSimpleUnionMockNameExpectationOrigins struct { 80 | origin string 81 | originT1 string 82 | } 83 | 84 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 85 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 86 | // Optional() makes method check to work in '0 or more' mode. 87 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 88 | // catch the problems when the expected method call is totally skipped during test run. 89 | func (mmName *mGenericSimpleUnionMockName[T]) Optional() *mGenericSimpleUnionMockName[T] { 90 | mmName.optional = true 91 | return mmName 92 | } 93 | 94 | // Expect sets up expected params for genericSimpleUnion.Name 95 | func (mmName *mGenericSimpleUnionMockName[T]) Expect(t1 T) *mGenericSimpleUnionMockName[T] { 96 | if mmName.mock.funcName != nil { 97 | mmName.mock.t.Fatalf("GenericSimpleUnionMock.Name mock is already set by Set") 98 | } 99 | 100 | if mmName.defaultExpectation == nil { 101 | mmName.defaultExpectation = &GenericSimpleUnionMockNameExpectation[T]{} 102 | } 103 | 104 | if mmName.defaultExpectation.paramPtrs != nil { 105 | mmName.mock.t.Fatalf("GenericSimpleUnionMock.Name mock is already set by ExpectParams functions") 106 | } 107 | 108 | mmName.defaultExpectation.params = &GenericSimpleUnionMockNameParams[T]{t1} 109 | mmName.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) 110 | for _, e := range mmName.expectations { 111 | if minimock.Equal(e.params, mmName.defaultExpectation.params) { 112 | mmName.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmName.defaultExpectation.params) 113 | } 114 | } 115 | 116 | return mmName 117 | } 118 | 119 | // ExpectT1Param1 sets up expected param t1 for genericSimpleUnion.Name 120 | func (mmName *mGenericSimpleUnionMockName[T]) ExpectT1Param1(t1 T) *mGenericSimpleUnionMockName[T] { 121 | if mmName.mock.funcName != nil { 122 | mmName.mock.t.Fatalf("GenericSimpleUnionMock.Name mock is already set by Set") 123 | } 124 | 125 | if mmName.defaultExpectation == nil { 126 | mmName.defaultExpectation = &GenericSimpleUnionMockNameExpectation[T]{} 127 | } 128 | 129 | if mmName.defaultExpectation.params != nil { 130 | mmName.mock.t.Fatalf("GenericSimpleUnionMock.Name mock is already set by Expect") 131 | } 132 | 133 | if mmName.defaultExpectation.paramPtrs == nil { 134 | mmName.defaultExpectation.paramPtrs = &GenericSimpleUnionMockNameParamPtrs[T]{} 135 | } 136 | mmName.defaultExpectation.paramPtrs.t1 = &t1 137 | mmName.defaultExpectation.expectationOrigins.originT1 = minimock.CallerInfo(1) 138 | 139 | return mmName 140 | } 141 | 142 | // Inspect accepts an inspector function that has same arguments as the genericSimpleUnion.Name 143 | func (mmName *mGenericSimpleUnionMockName[T]) Inspect(f func(t1 T)) *mGenericSimpleUnionMockName[T] { 144 | if mmName.mock.inspectFuncName != nil { 145 | mmName.mock.t.Fatalf("Inspect function is already set for GenericSimpleUnionMock.Name") 146 | } 147 | 148 | mmName.mock.inspectFuncName = f 149 | 150 | return mmName 151 | } 152 | 153 | // Return sets up results that will be returned by genericSimpleUnion.Name 154 | func (mmName *mGenericSimpleUnionMockName[T]) Return() *GenericSimpleUnionMock[T] { 155 | if mmName.mock.funcName != nil { 156 | mmName.mock.t.Fatalf("GenericSimpleUnionMock.Name mock is already set by Set") 157 | } 158 | 159 | if mmName.defaultExpectation == nil { 160 | mmName.defaultExpectation = &GenericSimpleUnionMockNameExpectation[T]{mock: mmName.mock} 161 | } 162 | 163 | mmName.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 164 | return mmName.mock 165 | } 166 | 167 | // Set uses given function f to mock the genericSimpleUnion.Name method 168 | func (mmName *mGenericSimpleUnionMockName[T]) Set(f func(t1 T)) *GenericSimpleUnionMock[T] { 169 | if mmName.defaultExpectation != nil { 170 | mmName.mock.t.Fatalf("Default expectation is already set for the genericSimpleUnion.Name method") 171 | } 172 | 173 | if len(mmName.expectations) > 0 { 174 | mmName.mock.t.Fatalf("Some expectations are already set for the genericSimpleUnion.Name method") 175 | } 176 | 177 | mmName.mock.funcName = f 178 | mmName.mock.funcNameOrigin = minimock.CallerInfo(1) 179 | return mmName.mock 180 | } 181 | 182 | // When sets expectation for the genericSimpleUnion.Name which will trigger the result defined by the following 183 | // Then helper 184 | func (mmName *mGenericSimpleUnionMockName[T]) When(t1 T) *GenericSimpleUnionMockNameExpectation[T] { 185 | if mmName.mock.funcName != nil { 186 | mmName.mock.t.Fatalf("GenericSimpleUnionMock.Name mock is already set by Set") 187 | } 188 | 189 | expectation := &GenericSimpleUnionMockNameExpectation[T]{ 190 | mock: mmName.mock, 191 | params: &GenericSimpleUnionMockNameParams[T]{t1}, 192 | expectationOrigins: GenericSimpleUnionMockNameExpectationOrigins{origin: minimock.CallerInfo(1)}, 193 | } 194 | mmName.expectations = append(mmName.expectations, expectation) 195 | return expectation 196 | } 197 | 198 | // Then sets up genericSimpleUnion.Name return parameters for the expectation previously defined by the When method 199 | 200 | func (e *GenericSimpleUnionMockNameExpectation[T]) Then() *GenericSimpleUnionMock[T] { 201 | return e.mock 202 | } 203 | 204 | // Times sets number of times genericSimpleUnion.Name should be invoked 205 | func (mmName *mGenericSimpleUnionMockName[T]) Times(n uint64) *mGenericSimpleUnionMockName[T] { 206 | if n == 0 { 207 | mmName.mock.t.Fatalf("Times of GenericSimpleUnionMock.Name mock can not be zero") 208 | } 209 | mm_atomic.StoreUint64(&mmName.expectedInvocations, n) 210 | mmName.expectedInvocationsOrigin = minimock.CallerInfo(1) 211 | return mmName 212 | } 213 | 214 | func (mmName *mGenericSimpleUnionMockName[T]) invocationsDone() bool { 215 | if len(mmName.expectations) == 0 && mmName.defaultExpectation == nil && mmName.mock.funcName == nil { 216 | return true 217 | } 218 | 219 | totalInvocations := mm_atomic.LoadUint64(&mmName.mock.afterNameCounter) 220 | expectedInvocations := mm_atomic.LoadUint64(&mmName.expectedInvocations) 221 | 222 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 223 | } 224 | 225 | // Name implements genericSimpleUnion 226 | func (mmName *GenericSimpleUnionMock[T]) Name(t1 T) { 227 | mm_atomic.AddUint64(&mmName.beforeNameCounter, 1) 228 | defer mm_atomic.AddUint64(&mmName.afterNameCounter, 1) 229 | 230 | mmName.t.Helper() 231 | 232 | if mmName.inspectFuncName != nil { 233 | mmName.inspectFuncName(t1) 234 | } 235 | 236 | mm_params := GenericSimpleUnionMockNameParams[T]{t1} 237 | 238 | // Record call args 239 | mmName.NameMock.mutex.Lock() 240 | mmName.NameMock.callArgs = append(mmName.NameMock.callArgs, &mm_params) 241 | mmName.NameMock.mutex.Unlock() 242 | 243 | for _, e := range mmName.NameMock.expectations { 244 | if minimock.Equal(*e.params, mm_params) { 245 | mm_atomic.AddUint64(&e.Counter, 1) 246 | return 247 | } 248 | } 249 | 250 | if mmName.NameMock.defaultExpectation != nil { 251 | mm_atomic.AddUint64(&mmName.NameMock.defaultExpectation.Counter, 1) 252 | mm_want := mmName.NameMock.defaultExpectation.params 253 | mm_want_ptrs := mmName.NameMock.defaultExpectation.paramPtrs 254 | 255 | mm_got := GenericSimpleUnionMockNameParams[T]{t1} 256 | 257 | if mm_want_ptrs != nil { 258 | 259 | if mm_want_ptrs.t1 != nil && !minimock.Equal(*mm_want_ptrs.t1, mm_got.t1) { 260 | mmName.t.Errorf("GenericSimpleUnionMock.Name got unexpected parameter t1, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 261 | mmName.NameMock.defaultExpectation.expectationOrigins.originT1, *mm_want_ptrs.t1, mm_got.t1, minimock.Diff(*mm_want_ptrs.t1, mm_got.t1)) 262 | } 263 | 264 | } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { 265 | mmName.t.Errorf("GenericSimpleUnionMock.Name got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 266 | mmName.NameMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) 267 | } 268 | 269 | return 270 | 271 | } 272 | if mmName.funcName != nil { 273 | mmName.funcName(t1) 274 | return 275 | } 276 | mmName.t.Fatalf("Unexpected call to GenericSimpleUnionMock.Name. %v", t1) 277 | 278 | } 279 | 280 | // NameAfterCounter returns a count of finished GenericSimpleUnionMock.Name invocations 281 | func (mmName *GenericSimpleUnionMock[T]) NameAfterCounter() uint64 { 282 | return mm_atomic.LoadUint64(&mmName.afterNameCounter) 283 | } 284 | 285 | // NameBeforeCounter returns a count of GenericSimpleUnionMock.Name invocations 286 | func (mmName *GenericSimpleUnionMock[T]) NameBeforeCounter() uint64 { 287 | return mm_atomic.LoadUint64(&mmName.beforeNameCounter) 288 | } 289 | 290 | // Calls returns a list of arguments used in each call to GenericSimpleUnionMock.Name. 291 | // The list is in the same order as the calls were made (i.e. recent calls have a higher index) 292 | func (mmName *mGenericSimpleUnionMockName[T]) Calls() []*GenericSimpleUnionMockNameParams[T] { 293 | mmName.mutex.RLock() 294 | 295 | argCopy := make([]*GenericSimpleUnionMockNameParams[T], len(mmName.callArgs)) 296 | copy(argCopy, mmName.callArgs) 297 | 298 | mmName.mutex.RUnlock() 299 | 300 | return argCopy 301 | } 302 | 303 | // MinimockNameDone returns true if the count of the Name invocations corresponds 304 | // the number of defined expectations 305 | func (m *GenericSimpleUnionMock[T]) MinimockNameDone() bool { 306 | if m.NameMock.optional { 307 | // Optional methods provide '0 or more' call count restriction. 308 | return true 309 | } 310 | 311 | for _, e := range m.NameMock.expectations { 312 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 313 | return false 314 | } 315 | } 316 | 317 | return m.NameMock.invocationsDone() 318 | } 319 | 320 | // MinimockNameInspect logs each unmet expectation 321 | func (m *GenericSimpleUnionMock[T]) MinimockNameInspect() { 322 | for _, e := range m.NameMock.expectations { 323 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 324 | m.t.Errorf("Expected call to GenericSimpleUnionMock.Name at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) 325 | } 326 | } 327 | 328 | afterNameCounter := mm_atomic.LoadUint64(&m.afterNameCounter) 329 | // if default expectation was set then invocations count should be greater than zero 330 | if m.NameMock.defaultExpectation != nil && afterNameCounter < 1 { 331 | if m.NameMock.defaultExpectation.params == nil { 332 | m.t.Errorf("Expected call to GenericSimpleUnionMock.Name at\n%s", m.NameMock.defaultExpectation.returnOrigin) 333 | } else { 334 | m.t.Errorf("Expected call to GenericSimpleUnionMock.Name at\n%s with params: %#v", m.NameMock.defaultExpectation.expectationOrigins.origin, *m.NameMock.defaultExpectation.params) 335 | } 336 | } 337 | // if func was set then invocations count should be greater than zero 338 | if m.funcName != nil && afterNameCounter < 1 { 339 | m.t.Errorf("Expected call to GenericSimpleUnionMock.Name at\n%s", m.funcNameOrigin) 340 | } 341 | 342 | if !m.NameMock.invocationsDone() && afterNameCounter > 0 { 343 | m.t.Errorf("Expected %d calls to GenericSimpleUnionMock.Name at\n%s but found %d calls", 344 | mm_atomic.LoadUint64(&m.NameMock.expectedInvocations), m.NameMock.expectedInvocationsOrigin, afterNameCounter) 345 | } 346 | } 347 | 348 | // MinimockFinish checks that all mocked methods have been called the expected number of times 349 | func (m *GenericSimpleUnionMock[T]) MinimockFinish() { 350 | m.finishOnce.Do(func() { 351 | if !m.minimockDone() { 352 | m.MinimockNameInspect() 353 | } 354 | }) 355 | } 356 | 357 | // MinimockWait waits for all mocked methods to be called the expected number of times 358 | func (m *GenericSimpleUnionMock[T]) MinimockWait(timeout mm_time.Duration) { 359 | timeoutCh := mm_time.After(timeout) 360 | for { 361 | if m.minimockDone() { 362 | return 363 | } 364 | select { 365 | case <-timeoutCh: 366 | m.MinimockFinish() 367 | return 368 | case <-mm_time.After(10 * mm_time.Millisecond): 369 | } 370 | } 371 | } 372 | 373 | func (m *GenericSimpleUnionMock[T]) minimockDone() bool { 374 | done := true 375 | return done && 376 | m.MinimockNameDone() 377 | } 378 | -------------------------------------------------------------------------------- /tests/generic_inline_union.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate minimock -i github.com/gojuno/minimock/v3/tests.genericInlineUnion -o generic_inline_union.go -n GenericInlineUnionMock -p tests 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | ) 14 | 15 | // GenericInlineUnionMock implements genericInlineUnion 16 | type GenericInlineUnionMock[T int | float64] struct { 17 | t minimock.Tester 18 | finishOnce sync.Once 19 | 20 | funcName func(t1 T) 21 | funcNameOrigin string 22 | inspectFuncName func(t1 T) 23 | afterNameCounter uint64 24 | beforeNameCounter uint64 25 | NameMock mGenericInlineUnionMockName[T] 26 | } 27 | 28 | // NewGenericInlineUnionMock returns a mock for genericInlineUnion 29 | func NewGenericInlineUnionMock[T int | float64](t minimock.Tester) *GenericInlineUnionMock[T] { 30 | m := &GenericInlineUnionMock[T]{t: t} 31 | 32 | if controller, ok := t.(minimock.MockController); ok { 33 | controller.RegisterMocker(m) 34 | } 35 | 36 | m.NameMock = mGenericInlineUnionMockName[T]{mock: m} 37 | m.NameMock.callArgs = []*GenericInlineUnionMockNameParams[T]{} 38 | 39 | t.Cleanup(m.MinimockFinish) 40 | 41 | return m 42 | } 43 | 44 | type mGenericInlineUnionMockName[T int | float64] struct { 45 | optional bool 46 | mock *GenericInlineUnionMock[T] 47 | defaultExpectation *GenericInlineUnionMockNameExpectation[T] 48 | expectations []*GenericInlineUnionMockNameExpectation[T] 49 | 50 | callArgs []*GenericInlineUnionMockNameParams[T] 51 | mutex sync.RWMutex 52 | 53 | expectedInvocations uint64 54 | expectedInvocationsOrigin string 55 | } 56 | 57 | // GenericInlineUnionMockNameExpectation specifies expectation struct of the genericInlineUnion.Name 58 | type GenericInlineUnionMockNameExpectation[T int | float64] struct { 59 | mock *GenericInlineUnionMock[T] 60 | params *GenericInlineUnionMockNameParams[T] 61 | paramPtrs *GenericInlineUnionMockNameParamPtrs[T] 62 | expectationOrigins GenericInlineUnionMockNameExpectationOrigins 63 | 64 | returnOrigin string 65 | Counter uint64 66 | } 67 | 68 | // GenericInlineUnionMockNameParams contains parameters of the genericInlineUnion.Name 69 | type GenericInlineUnionMockNameParams[T int | float64] struct { 70 | t1 T 71 | } 72 | 73 | // GenericInlineUnionMockNameParamPtrs contains pointers to parameters of the genericInlineUnion.Name 74 | type GenericInlineUnionMockNameParamPtrs[T int | float64] struct { 75 | t1 *T 76 | } 77 | 78 | // GenericInlineUnionMockNameOrigins contains origins of expectations of the genericInlineUnion.Name 79 | type GenericInlineUnionMockNameExpectationOrigins struct { 80 | origin string 81 | originT1 string 82 | } 83 | 84 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 85 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 86 | // Optional() makes method check to work in '0 or more' mode. 87 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 88 | // catch the problems when the expected method call is totally skipped during test run. 89 | func (mmName *mGenericInlineUnionMockName[T]) Optional() *mGenericInlineUnionMockName[T] { 90 | mmName.optional = true 91 | return mmName 92 | } 93 | 94 | // Expect sets up expected params for genericInlineUnion.Name 95 | func (mmName *mGenericInlineUnionMockName[T]) Expect(t1 T) *mGenericInlineUnionMockName[T] { 96 | if mmName.mock.funcName != nil { 97 | mmName.mock.t.Fatalf("GenericInlineUnionMock.Name mock is already set by Set") 98 | } 99 | 100 | if mmName.defaultExpectation == nil { 101 | mmName.defaultExpectation = &GenericInlineUnionMockNameExpectation[T]{} 102 | } 103 | 104 | if mmName.defaultExpectation.paramPtrs != nil { 105 | mmName.mock.t.Fatalf("GenericInlineUnionMock.Name mock is already set by ExpectParams functions") 106 | } 107 | 108 | mmName.defaultExpectation.params = &GenericInlineUnionMockNameParams[T]{t1} 109 | mmName.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) 110 | for _, e := range mmName.expectations { 111 | if minimock.Equal(e.params, mmName.defaultExpectation.params) { 112 | mmName.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmName.defaultExpectation.params) 113 | } 114 | } 115 | 116 | return mmName 117 | } 118 | 119 | // ExpectT1Param1 sets up expected param t1 for genericInlineUnion.Name 120 | func (mmName *mGenericInlineUnionMockName[T]) ExpectT1Param1(t1 T) *mGenericInlineUnionMockName[T] { 121 | if mmName.mock.funcName != nil { 122 | mmName.mock.t.Fatalf("GenericInlineUnionMock.Name mock is already set by Set") 123 | } 124 | 125 | if mmName.defaultExpectation == nil { 126 | mmName.defaultExpectation = &GenericInlineUnionMockNameExpectation[T]{} 127 | } 128 | 129 | if mmName.defaultExpectation.params != nil { 130 | mmName.mock.t.Fatalf("GenericInlineUnionMock.Name mock is already set by Expect") 131 | } 132 | 133 | if mmName.defaultExpectation.paramPtrs == nil { 134 | mmName.defaultExpectation.paramPtrs = &GenericInlineUnionMockNameParamPtrs[T]{} 135 | } 136 | mmName.defaultExpectation.paramPtrs.t1 = &t1 137 | mmName.defaultExpectation.expectationOrigins.originT1 = minimock.CallerInfo(1) 138 | 139 | return mmName 140 | } 141 | 142 | // Inspect accepts an inspector function that has same arguments as the genericInlineUnion.Name 143 | func (mmName *mGenericInlineUnionMockName[T]) Inspect(f func(t1 T)) *mGenericInlineUnionMockName[T] { 144 | if mmName.mock.inspectFuncName != nil { 145 | mmName.mock.t.Fatalf("Inspect function is already set for GenericInlineUnionMock.Name") 146 | } 147 | 148 | mmName.mock.inspectFuncName = f 149 | 150 | return mmName 151 | } 152 | 153 | // Return sets up results that will be returned by genericInlineUnion.Name 154 | func (mmName *mGenericInlineUnionMockName[T]) Return() *GenericInlineUnionMock[T] { 155 | if mmName.mock.funcName != nil { 156 | mmName.mock.t.Fatalf("GenericInlineUnionMock.Name mock is already set by Set") 157 | } 158 | 159 | if mmName.defaultExpectation == nil { 160 | mmName.defaultExpectation = &GenericInlineUnionMockNameExpectation[T]{mock: mmName.mock} 161 | } 162 | 163 | mmName.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 164 | return mmName.mock 165 | } 166 | 167 | // Set uses given function f to mock the genericInlineUnion.Name method 168 | func (mmName *mGenericInlineUnionMockName[T]) Set(f func(t1 T)) *GenericInlineUnionMock[T] { 169 | if mmName.defaultExpectation != nil { 170 | mmName.mock.t.Fatalf("Default expectation is already set for the genericInlineUnion.Name method") 171 | } 172 | 173 | if len(mmName.expectations) > 0 { 174 | mmName.mock.t.Fatalf("Some expectations are already set for the genericInlineUnion.Name method") 175 | } 176 | 177 | mmName.mock.funcName = f 178 | mmName.mock.funcNameOrigin = minimock.CallerInfo(1) 179 | return mmName.mock 180 | } 181 | 182 | // When sets expectation for the genericInlineUnion.Name which will trigger the result defined by the following 183 | // Then helper 184 | func (mmName *mGenericInlineUnionMockName[T]) When(t1 T) *GenericInlineUnionMockNameExpectation[T] { 185 | if mmName.mock.funcName != nil { 186 | mmName.mock.t.Fatalf("GenericInlineUnionMock.Name mock is already set by Set") 187 | } 188 | 189 | expectation := &GenericInlineUnionMockNameExpectation[T]{ 190 | mock: mmName.mock, 191 | params: &GenericInlineUnionMockNameParams[T]{t1}, 192 | expectationOrigins: GenericInlineUnionMockNameExpectationOrigins{origin: minimock.CallerInfo(1)}, 193 | } 194 | mmName.expectations = append(mmName.expectations, expectation) 195 | return expectation 196 | } 197 | 198 | // Then sets up genericInlineUnion.Name return parameters for the expectation previously defined by the When method 199 | 200 | func (e *GenericInlineUnionMockNameExpectation[T]) Then() *GenericInlineUnionMock[T] { 201 | return e.mock 202 | } 203 | 204 | // Times sets number of times genericInlineUnion.Name should be invoked 205 | func (mmName *mGenericInlineUnionMockName[T]) Times(n uint64) *mGenericInlineUnionMockName[T] { 206 | if n == 0 { 207 | mmName.mock.t.Fatalf("Times of GenericInlineUnionMock.Name mock can not be zero") 208 | } 209 | mm_atomic.StoreUint64(&mmName.expectedInvocations, n) 210 | mmName.expectedInvocationsOrigin = minimock.CallerInfo(1) 211 | return mmName 212 | } 213 | 214 | func (mmName *mGenericInlineUnionMockName[T]) invocationsDone() bool { 215 | if len(mmName.expectations) == 0 && mmName.defaultExpectation == nil && mmName.mock.funcName == nil { 216 | return true 217 | } 218 | 219 | totalInvocations := mm_atomic.LoadUint64(&mmName.mock.afterNameCounter) 220 | expectedInvocations := mm_atomic.LoadUint64(&mmName.expectedInvocations) 221 | 222 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 223 | } 224 | 225 | // Name implements genericInlineUnion 226 | func (mmName *GenericInlineUnionMock[T]) Name(t1 T) { 227 | mm_atomic.AddUint64(&mmName.beforeNameCounter, 1) 228 | defer mm_atomic.AddUint64(&mmName.afterNameCounter, 1) 229 | 230 | mmName.t.Helper() 231 | 232 | if mmName.inspectFuncName != nil { 233 | mmName.inspectFuncName(t1) 234 | } 235 | 236 | mm_params := GenericInlineUnionMockNameParams[T]{t1} 237 | 238 | // Record call args 239 | mmName.NameMock.mutex.Lock() 240 | mmName.NameMock.callArgs = append(mmName.NameMock.callArgs, &mm_params) 241 | mmName.NameMock.mutex.Unlock() 242 | 243 | for _, e := range mmName.NameMock.expectations { 244 | if minimock.Equal(*e.params, mm_params) { 245 | mm_atomic.AddUint64(&e.Counter, 1) 246 | return 247 | } 248 | } 249 | 250 | if mmName.NameMock.defaultExpectation != nil { 251 | mm_atomic.AddUint64(&mmName.NameMock.defaultExpectation.Counter, 1) 252 | mm_want := mmName.NameMock.defaultExpectation.params 253 | mm_want_ptrs := mmName.NameMock.defaultExpectation.paramPtrs 254 | 255 | mm_got := GenericInlineUnionMockNameParams[T]{t1} 256 | 257 | if mm_want_ptrs != nil { 258 | 259 | if mm_want_ptrs.t1 != nil && !minimock.Equal(*mm_want_ptrs.t1, mm_got.t1) { 260 | mmName.t.Errorf("GenericInlineUnionMock.Name got unexpected parameter t1, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 261 | mmName.NameMock.defaultExpectation.expectationOrigins.originT1, *mm_want_ptrs.t1, mm_got.t1, minimock.Diff(*mm_want_ptrs.t1, mm_got.t1)) 262 | } 263 | 264 | } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { 265 | mmName.t.Errorf("GenericInlineUnionMock.Name got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 266 | mmName.NameMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) 267 | } 268 | 269 | return 270 | 271 | } 272 | if mmName.funcName != nil { 273 | mmName.funcName(t1) 274 | return 275 | } 276 | mmName.t.Fatalf("Unexpected call to GenericInlineUnionMock.Name. %v", t1) 277 | 278 | } 279 | 280 | // NameAfterCounter returns a count of finished GenericInlineUnionMock.Name invocations 281 | func (mmName *GenericInlineUnionMock[T]) NameAfterCounter() uint64 { 282 | return mm_atomic.LoadUint64(&mmName.afterNameCounter) 283 | } 284 | 285 | // NameBeforeCounter returns a count of GenericInlineUnionMock.Name invocations 286 | func (mmName *GenericInlineUnionMock[T]) NameBeforeCounter() uint64 { 287 | return mm_atomic.LoadUint64(&mmName.beforeNameCounter) 288 | } 289 | 290 | // Calls returns a list of arguments used in each call to GenericInlineUnionMock.Name. 291 | // The list is in the same order as the calls were made (i.e. recent calls have a higher index) 292 | func (mmName *mGenericInlineUnionMockName[T]) Calls() []*GenericInlineUnionMockNameParams[T] { 293 | mmName.mutex.RLock() 294 | 295 | argCopy := make([]*GenericInlineUnionMockNameParams[T], len(mmName.callArgs)) 296 | copy(argCopy, mmName.callArgs) 297 | 298 | mmName.mutex.RUnlock() 299 | 300 | return argCopy 301 | } 302 | 303 | // MinimockNameDone returns true if the count of the Name invocations corresponds 304 | // the number of defined expectations 305 | func (m *GenericInlineUnionMock[T]) MinimockNameDone() bool { 306 | if m.NameMock.optional { 307 | // Optional methods provide '0 or more' call count restriction. 308 | return true 309 | } 310 | 311 | for _, e := range m.NameMock.expectations { 312 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 313 | return false 314 | } 315 | } 316 | 317 | return m.NameMock.invocationsDone() 318 | } 319 | 320 | // MinimockNameInspect logs each unmet expectation 321 | func (m *GenericInlineUnionMock[T]) MinimockNameInspect() { 322 | for _, e := range m.NameMock.expectations { 323 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 324 | m.t.Errorf("Expected call to GenericInlineUnionMock.Name at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) 325 | } 326 | } 327 | 328 | afterNameCounter := mm_atomic.LoadUint64(&m.afterNameCounter) 329 | // if default expectation was set then invocations count should be greater than zero 330 | if m.NameMock.defaultExpectation != nil && afterNameCounter < 1 { 331 | if m.NameMock.defaultExpectation.params == nil { 332 | m.t.Errorf("Expected call to GenericInlineUnionMock.Name at\n%s", m.NameMock.defaultExpectation.returnOrigin) 333 | } else { 334 | m.t.Errorf("Expected call to GenericInlineUnionMock.Name at\n%s with params: %#v", m.NameMock.defaultExpectation.expectationOrigins.origin, *m.NameMock.defaultExpectation.params) 335 | } 336 | } 337 | // if func was set then invocations count should be greater than zero 338 | if m.funcName != nil && afterNameCounter < 1 { 339 | m.t.Errorf("Expected call to GenericInlineUnionMock.Name at\n%s", m.funcNameOrigin) 340 | } 341 | 342 | if !m.NameMock.invocationsDone() && afterNameCounter > 0 { 343 | m.t.Errorf("Expected %d calls to GenericInlineUnionMock.Name at\n%s but found %d calls", 344 | mm_atomic.LoadUint64(&m.NameMock.expectedInvocations), m.NameMock.expectedInvocationsOrigin, afterNameCounter) 345 | } 346 | } 347 | 348 | // MinimockFinish checks that all mocked methods have been called the expected number of times 349 | func (m *GenericInlineUnionMock[T]) MinimockFinish() { 350 | m.finishOnce.Do(func() { 351 | if !m.minimockDone() { 352 | m.MinimockNameInspect() 353 | } 354 | }) 355 | } 356 | 357 | // MinimockWait waits for all mocked methods to be called the expected number of times 358 | func (m *GenericInlineUnionMock[T]) MinimockWait(timeout mm_time.Duration) { 359 | timeoutCh := mm_time.After(timeout) 360 | for { 361 | if m.minimockDone() { 362 | return 363 | } 364 | select { 365 | case <-timeoutCh: 366 | m.MinimockFinish() 367 | return 368 | case <-mm_time.After(10 * mm_time.Millisecond): 369 | } 370 | } 371 | } 372 | 373 | func (m *GenericInlineUnionMock[T]) minimockDone() bool { 374 | done := true 375 | return done && 376 | m.MinimockNameDone() 377 | } 378 | -------------------------------------------------------------------------------- /tests/generic_complex_union.go: -------------------------------------------------------------------------------- 1 | // Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. 2 | 3 | package tests 4 | 5 | //go:generate minimock -i github.com/gojuno/minimock/v3/tests.genericComplexUnion -o generic_complex_union.go -n GenericComplexUnionMock -p tests 6 | 7 | import ( 8 | "sync" 9 | mm_atomic "sync/atomic" 10 | mm_time "time" 11 | 12 | "github.com/gojuno/minimock/v3" 13 | ) 14 | 15 | // GenericComplexUnionMock implements genericComplexUnion 16 | type GenericComplexUnionMock[T complexUnion] struct { 17 | t minimock.Tester 18 | finishOnce sync.Once 19 | 20 | funcName func(t1 T) 21 | funcNameOrigin string 22 | inspectFuncName func(t1 T) 23 | afterNameCounter uint64 24 | beforeNameCounter uint64 25 | NameMock mGenericComplexUnionMockName[T] 26 | } 27 | 28 | // NewGenericComplexUnionMock returns a mock for genericComplexUnion 29 | func NewGenericComplexUnionMock[T complexUnion](t minimock.Tester) *GenericComplexUnionMock[T] { 30 | m := &GenericComplexUnionMock[T]{t: t} 31 | 32 | if controller, ok := t.(minimock.MockController); ok { 33 | controller.RegisterMocker(m) 34 | } 35 | 36 | m.NameMock = mGenericComplexUnionMockName[T]{mock: m} 37 | m.NameMock.callArgs = []*GenericComplexUnionMockNameParams[T]{} 38 | 39 | t.Cleanup(m.MinimockFinish) 40 | 41 | return m 42 | } 43 | 44 | type mGenericComplexUnionMockName[T complexUnion] struct { 45 | optional bool 46 | mock *GenericComplexUnionMock[T] 47 | defaultExpectation *GenericComplexUnionMockNameExpectation[T] 48 | expectations []*GenericComplexUnionMockNameExpectation[T] 49 | 50 | callArgs []*GenericComplexUnionMockNameParams[T] 51 | mutex sync.RWMutex 52 | 53 | expectedInvocations uint64 54 | expectedInvocationsOrigin string 55 | } 56 | 57 | // GenericComplexUnionMockNameExpectation specifies expectation struct of the genericComplexUnion.Name 58 | type GenericComplexUnionMockNameExpectation[T complexUnion] struct { 59 | mock *GenericComplexUnionMock[T] 60 | params *GenericComplexUnionMockNameParams[T] 61 | paramPtrs *GenericComplexUnionMockNameParamPtrs[T] 62 | expectationOrigins GenericComplexUnionMockNameExpectationOrigins 63 | 64 | returnOrigin string 65 | Counter uint64 66 | } 67 | 68 | // GenericComplexUnionMockNameParams contains parameters of the genericComplexUnion.Name 69 | type GenericComplexUnionMockNameParams[T complexUnion] struct { 70 | t1 T 71 | } 72 | 73 | // GenericComplexUnionMockNameParamPtrs contains pointers to parameters of the genericComplexUnion.Name 74 | type GenericComplexUnionMockNameParamPtrs[T complexUnion] struct { 75 | t1 *T 76 | } 77 | 78 | // GenericComplexUnionMockNameOrigins contains origins of expectations of the genericComplexUnion.Name 79 | type GenericComplexUnionMockNameExpectationOrigins struct { 80 | origin string 81 | originT1 string 82 | } 83 | 84 | // Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning 85 | // the test will fail minimock's automatic final call check if the mocked method was not called at least once. 86 | // Optional() makes method check to work in '0 or more' mode. 87 | // It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to 88 | // catch the problems when the expected method call is totally skipped during test run. 89 | func (mmName *mGenericComplexUnionMockName[T]) Optional() *mGenericComplexUnionMockName[T] { 90 | mmName.optional = true 91 | return mmName 92 | } 93 | 94 | // Expect sets up expected params for genericComplexUnion.Name 95 | func (mmName *mGenericComplexUnionMockName[T]) Expect(t1 T) *mGenericComplexUnionMockName[T] { 96 | if mmName.mock.funcName != nil { 97 | mmName.mock.t.Fatalf("GenericComplexUnionMock.Name mock is already set by Set") 98 | } 99 | 100 | if mmName.defaultExpectation == nil { 101 | mmName.defaultExpectation = &GenericComplexUnionMockNameExpectation[T]{} 102 | } 103 | 104 | if mmName.defaultExpectation.paramPtrs != nil { 105 | mmName.mock.t.Fatalf("GenericComplexUnionMock.Name mock is already set by ExpectParams functions") 106 | } 107 | 108 | mmName.defaultExpectation.params = &GenericComplexUnionMockNameParams[T]{t1} 109 | mmName.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) 110 | for _, e := range mmName.expectations { 111 | if minimock.Equal(e.params, mmName.defaultExpectation.params) { 112 | mmName.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmName.defaultExpectation.params) 113 | } 114 | } 115 | 116 | return mmName 117 | } 118 | 119 | // ExpectT1Param1 sets up expected param t1 for genericComplexUnion.Name 120 | func (mmName *mGenericComplexUnionMockName[T]) ExpectT1Param1(t1 T) *mGenericComplexUnionMockName[T] { 121 | if mmName.mock.funcName != nil { 122 | mmName.mock.t.Fatalf("GenericComplexUnionMock.Name mock is already set by Set") 123 | } 124 | 125 | if mmName.defaultExpectation == nil { 126 | mmName.defaultExpectation = &GenericComplexUnionMockNameExpectation[T]{} 127 | } 128 | 129 | if mmName.defaultExpectation.params != nil { 130 | mmName.mock.t.Fatalf("GenericComplexUnionMock.Name mock is already set by Expect") 131 | } 132 | 133 | if mmName.defaultExpectation.paramPtrs == nil { 134 | mmName.defaultExpectation.paramPtrs = &GenericComplexUnionMockNameParamPtrs[T]{} 135 | } 136 | mmName.defaultExpectation.paramPtrs.t1 = &t1 137 | mmName.defaultExpectation.expectationOrigins.originT1 = minimock.CallerInfo(1) 138 | 139 | return mmName 140 | } 141 | 142 | // Inspect accepts an inspector function that has same arguments as the genericComplexUnion.Name 143 | func (mmName *mGenericComplexUnionMockName[T]) Inspect(f func(t1 T)) *mGenericComplexUnionMockName[T] { 144 | if mmName.mock.inspectFuncName != nil { 145 | mmName.mock.t.Fatalf("Inspect function is already set for GenericComplexUnionMock.Name") 146 | } 147 | 148 | mmName.mock.inspectFuncName = f 149 | 150 | return mmName 151 | } 152 | 153 | // Return sets up results that will be returned by genericComplexUnion.Name 154 | func (mmName *mGenericComplexUnionMockName[T]) Return() *GenericComplexUnionMock[T] { 155 | if mmName.mock.funcName != nil { 156 | mmName.mock.t.Fatalf("GenericComplexUnionMock.Name mock is already set by Set") 157 | } 158 | 159 | if mmName.defaultExpectation == nil { 160 | mmName.defaultExpectation = &GenericComplexUnionMockNameExpectation[T]{mock: mmName.mock} 161 | } 162 | 163 | mmName.defaultExpectation.returnOrigin = minimock.CallerInfo(1) 164 | return mmName.mock 165 | } 166 | 167 | // Set uses given function f to mock the genericComplexUnion.Name method 168 | func (mmName *mGenericComplexUnionMockName[T]) Set(f func(t1 T)) *GenericComplexUnionMock[T] { 169 | if mmName.defaultExpectation != nil { 170 | mmName.mock.t.Fatalf("Default expectation is already set for the genericComplexUnion.Name method") 171 | } 172 | 173 | if len(mmName.expectations) > 0 { 174 | mmName.mock.t.Fatalf("Some expectations are already set for the genericComplexUnion.Name method") 175 | } 176 | 177 | mmName.mock.funcName = f 178 | mmName.mock.funcNameOrigin = minimock.CallerInfo(1) 179 | return mmName.mock 180 | } 181 | 182 | // When sets expectation for the genericComplexUnion.Name which will trigger the result defined by the following 183 | // Then helper 184 | func (mmName *mGenericComplexUnionMockName[T]) When(t1 T) *GenericComplexUnionMockNameExpectation[T] { 185 | if mmName.mock.funcName != nil { 186 | mmName.mock.t.Fatalf("GenericComplexUnionMock.Name mock is already set by Set") 187 | } 188 | 189 | expectation := &GenericComplexUnionMockNameExpectation[T]{ 190 | mock: mmName.mock, 191 | params: &GenericComplexUnionMockNameParams[T]{t1}, 192 | expectationOrigins: GenericComplexUnionMockNameExpectationOrigins{origin: minimock.CallerInfo(1)}, 193 | } 194 | mmName.expectations = append(mmName.expectations, expectation) 195 | return expectation 196 | } 197 | 198 | // Then sets up genericComplexUnion.Name return parameters for the expectation previously defined by the When method 199 | 200 | func (e *GenericComplexUnionMockNameExpectation[T]) Then() *GenericComplexUnionMock[T] { 201 | return e.mock 202 | } 203 | 204 | // Times sets number of times genericComplexUnion.Name should be invoked 205 | func (mmName *mGenericComplexUnionMockName[T]) Times(n uint64) *mGenericComplexUnionMockName[T] { 206 | if n == 0 { 207 | mmName.mock.t.Fatalf("Times of GenericComplexUnionMock.Name mock can not be zero") 208 | } 209 | mm_atomic.StoreUint64(&mmName.expectedInvocations, n) 210 | mmName.expectedInvocationsOrigin = minimock.CallerInfo(1) 211 | return mmName 212 | } 213 | 214 | func (mmName *mGenericComplexUnionMockName[T]) invocationsDone() bool { 215 | if len(mmName.expectations) == 0 && mmName.defaultExpectation == nil && mmName.mock.funcName == nil { 216 | return true 217 | } 218 | 219 | totalInvocations := mm_atomic.LoadUint64(&mmName.mock.afterNameCounter) 220 | expectedInvocations := mm_atomic.LoadUint64(&mmName.expectedInvocations) 221 | 222 | return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) 223 | } 224 | 225 | // Name implements genericComplexUnion 226 | func (mmName *GenericComplexUnionMock[T]) Name(t1 T) { 227 | mm_atomic.AddUint64(&mmName.beforeNameCounter, 1) 228 | defer mm_atomic.AddUint64(&mmName.afterNameCounter, 1) 229 | 230 | mmName.t.Helper() 231 | 232 | if mmName.inspectFuncName != nil { 233 | mmName.inspectFuncName(t1) 234 | } 235 | 236 | mm_params := GenericComplexUnionMockNameParams[T]{t1} 237 | 238 | // Record call args 239 | mmName.NameMock.mutex.Lock() 240 | mmName.NameMock.callArgs = append(mmName.NameMock.callArgs, &mm_params) 241 | mmName.NameMock.mutex.Unlock() 242 | 243 | for _, e := range mmName.NameMock.expectations { 244 | if minimock.Equal(*e.params, mm_params) { 245 | mm_atomic.AddUint64(&e.Counter, 1) 246 | return 247 | } 248 | } 249 | 250 | if mmName.NameMock.defaultExpectation != nil { 251 | mm_atomic.AddUint64(&mmName.NameMock.defaultExpectation.Counter, 1) 252 | mm_want := mmName.NameMock.defaultExpectation.params 253 | mm_want_ptrs := mmName.NameMock.defaultExpectation.paramPtrs 254 | 255 | mm_got := GenericComplexUnionMockNameParams[T]{t1} 256 | 257 | if mm_want_ptrs != nil { 258 | 259 | if mm_want_ptrs.t1 != nil && !minimock.Equal(*mm_want_ptrs.t1, mm_got.t1) { 260 | mmName.t.Errorf("GenericComplexUnionMock.Name got unexpected parameter t1, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 261 | mmName.NameMock.defaultExpectation.expectationOrigins.originT1, *mm_want_ptrs.t1, mm_got.t1, minimock.Diff(*mm_want_ptrs.t1, mm_got.t1)) 262 | } 263 | 264 | } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { 265 | mmName.t.Errorf("GenericComplexUnionMock.Name got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", 266 | mmName.NameMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) 267 | } 268 | 269 | return 270 | 271 | } 272 | if mmName.funcName != nil { 273 | mmName.funcName(t1) 274 | return 275 | } 276 | mmName.t.Fatalf("Unexpected call to GenericComplexUnionMock.Name. %v", t1) 277 | 278 | } 279 | 280 | // NameAfterCounter returns a count of finished GenericComplexUnionMock.Name invocations 281 | func (mmName *GenericComplexUnionMock[T]) NameAfterCounter() uint64 { 282 | return mm_atomic.LoadUint64(&mmName.afterNameCounter) 283 | } 284 | 285 | // NameBeforeCounter returns a count of GenericComplexUnionMock.Name invocations 286 | func (mmName *GenericComplexUnionMock[T]) NameBeforeCounter() uint64 { 287 | return mm_atomic.LoadUint64(&mmName.beforeNameCounter) 288 | } 289 | 290 | // Calls returns a list of arguments used in each call to GenericComplexUnionMock.Name. 291 | // The list is in the same order as the calls were made (i.e. recent calls have a higher index) 292 | func (mmName *mGenericComplexUnionMockName[T]) Calls() []*GenericComplexUnionMockNameParams[T] { 293 | mmName.mutex.RLock() 294 | 295 | argCopy := make([]*GenericComplexUnionMockNameParams[T], len(mmName.callArgs)) 296 | copy(argCopy, mmName.callArgs) 297 | 298 | mmName.mutex.RUnlock() 299 | 300 | return argCopy 301 | } 302 | 303 | // MinimockNameDone returns true if the count of the Name invocations corresponds 304 | // the number of defined expectations 305 | func (m *GenericComplexUnionMock[T]) MinimockNameDone() bool { 306 | if m.NameMock.optional { 307 | // Optional methods provide '0 or more' call count restriction. 308 | return true 309 | } 310 | 311 | for _, e := range m.NameMock.expectations { 312 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 313 | return false 314 | } 315 | } 316 | 317 | return m.NameMock.invocationsDone() 318 | } 319 | 320 | // MinimockNameInspect logs each unmet expectation 321 | func (m *GenericComplexUnionMock[T]) MinimockNameInspect() { 322 | for _, e := range m.NameMock.expectations { 323 | if mm_atomic.LoadUint64(&e.Counter) < 1 { 324 | m.t.Errorf("Expected call to GenericComplexUnionMock.Name at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) 325 | } 326 | } 327 | 328 | afterNameCounter := mm_atomic.LoadUint64(&m.afterNameCounter) 329 | // if default expectation was set then invocations count should be greater than zero 330 | if m.NameMock.defaultExpectation != nil && afterNameCounter < 1 { 331 | if m.NameMock.defaultExpectation.params == nil { 332 | m.t.Errorf("Expected call to GenericComplexUnionMock.Name at\n%s", m.NameMock.defaultExpectation.returnOrigin) 333 | } else { 334 | m.t.Errorf("Expected call to GenericComplexUnionMock.Name at\n%s with params: %#v", m.NameMock.defaultExpectation.expectationOrigins.origin, *m.NameMock.defaultExpectation.params) 335 | } 336 | } 337 | // if func was set then invocations count should be greater than zero 338 | if m.funcName != nil && afterNameCounter < 1 { 339 | m.t.Errorf("Expected call to GenericComplexUnionMock.Name at\n%s", m.funcNameOrigin) 340 | } 341 | 342 | if !m.NameMock.invocationsDone() && afterNameCounter > 0 { 343 | m.t.Errorf("Expected %d calls to GenericComplexUnionMock.Name at\n%s but found %d calls", 344 | mm_atomic.LoadUint64(&m.NameMock.expectedInvocations), m.NameMock.expectedInvocationsOrigin, afterNameCounter) 345 | } 346 | } 347 | 348 | // MinimockFinish checks that all mocked methods have been called the expected number of times 349 | func (m *GenericComplexUnionMock[T]) MinimockFinish() { 350 | m.finishOnce.Do(func() { 351 | if !m.minimockDone() { 352 | m.MinimockNameInspect() 353 | } 354 | }) 355 | } 356 | 357 | // MinimockWait waits for all mocked methods to be called the expected number of times 358 | func (m *GenericComplexUnionMock[T]) MinimockWait(timeout mm_time.Duration) { 359 | timeoutCh := mm_time.After(timeout) 360 | for { 361 | if m.minimockDone() { 362 | return 363 | } 364 | select { 365 | case <-timeoutCh: 366 | m.MinimockFinish() 367 | return 368 | case <-mm_time.After(10 * mm_time.Millisecond): 369 | } 370 | } 371 | } 372 | 373 | func (m *GenericComplexUnionMock[T]) minimockDone() bool { 374 | done := true 375 | return done && 376 | m.MinimockNameDone() 377 | } 378 | -------------------------------------------------------------------------------- /cmd/minimock/minimock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "go/token" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "runtime/debug" 12 | "strings" 13 | "text/template" 14 | "time" 15 | "unicode" 16 | 17 | "github.com/gojuno/minimock/v3" 18 | "github.com/gojuno/minimock/v3/internal/types" 19 | "github.com/hexdigest/gowrap/generator" 20 | "github.com/hexdigest/gowrap/pkg" 21 | "github.com/pkg/errors" 22 | "golang.org/x/tools/go/packages" 23 | ) 24 | 25 | const devVersion = "dev" 26 | 27 | var ( 28 | //do not modify the following vars 29 | //the values are being injected at the compile time by goreleaser 30 | version string 31 | commit string 32 | buildDate = time.Now().Format(time.RFC3339) 33 | ) 34 | 35 | var helpers = template.FuncMap{ 36 | "title": func(s string) string { 37 | runes := []rune(s) 38 | for i, r := range runes { 39 | if unicode.IsLetter(r) { 40 | runes[i] = unicode.ToUpper(r) 41 | break 42 | } 43 | } 44 | return string(runes) 45 | }, 46 | "in": func(s string, in ...string) bool { 47 | s = strings.Trim(s, " ") 48 | for _, i := range in { 49 | if s != "" && strings.Contains(i, s) { 50 | return true 51 | } 52 | } 53 | return false 54 | }, 55 | "inc": func(a int) int { 56 | return a + 1 57 | }, 58 | } 59 | 60 | type ( 61 | options struct { 62 | interfaces []interfaceInfo 63 | noGenerate bool 64 | suffix string 65 | prefix string 66 | mockNames []string 67 | packageNames []string 68 | goGenerateGoRun bool 69 | } 70 | 71 | interfaceInfo struct { 72 | Type string 73 | ImportPath string 74 | WriteTo string 75 | } 76 | ) 77 | 78 | func init() { 79 | if buildInfo, ok := debug.ReadBuildInfo(); ok { 80 | // if installing directly with go build/install 81 | // take version and commit from buildInfo 82 | version = getVersion(version, buildInfo) 83 | commit = getCommit(commit, buildInfo) 84 | } 85 | // if goreleaser didn't set these vars, 86 | // and we didn't find buildInfo then set 87 | // them to 'dev' 88 | if version == "" { 89 | version = devVersion 90 | } 91 | if commit == "" { 92 | commit = devVersion 93 | } 94 | 95 | } 96 | 97 | func getCommit(commit string, buildInfo *debug.BuildInfo) string { 98 | if commit != "" { 99 | return commit 100 | } 101 | for _, setting := range buildInfo.Settings { 102 | if setting.Key == "vcs.revision" { 103 | return setting.Value 104 | } 105 | } 106 | 107 | return "" 108 | } 109 | 110 | func getVersion(version string, buildInfo *debug.BuildInfo) string { 111 | if version != "" { 112 | return version 113 | } 114 | if buildInfo.Main.Version != "" { 115 | return buildInfo.Main.Version 116 | } 117 | 118 | return "" 119 | } 120 | 121 | func main() { 122 | opts, err := processArgs(os.Args[1:], os.Stdout, os.Stderr) 123 | if err != nil { 124 | if err == errInvalidArguments { 125 | os.Exit(2) 126 | } 127 | 128 | die("%v", err) 129 | } 130 | 131 | if opts == nil { //help requested 132 | os.Exit(0) 133 | } 134 | 135 | if err = run(opts); err != nil { 136 | die("%v", err) 137 | } 138 | } 139 | 140 | func run(opts *options) (err error) { 141 | var ( 142 | sourcePackage *packages.Package 143 | astPackage *pkg.Package 144 | fs *token.FileSet 145 | ) 146 | 147 | for i, in := range opts.interfaces { 148 | if i == 0 || in.ImportPath != opts.interfaces[i-1].ImportPath { 149 | if sourcePackage, err = pkg.Load(in.ImportPath); err != nil { 150 | return err 151 | } 152 | 153 | fs = token.NewFileSet() 154 | if astPackage, err = pkg.AST(fs, sourcePackage); err != nil { 155 | return errors.Wrap(err, "failed to load package sources") 156 | } 157 | } 158 | 159 | interfaces := types.FindAllInterfaces(astPackage, in.Type) 160 | 161 | packageName := "" 162 | if len(opts.interfaces) == len(opts.packageNames) { 163 | packageName = opts.packageNames[i] 164 | } 165 | 166 | gopts := generator.Options{ 167 | SourcePackage: sourcePackage.PkgPath, 168 | SourcePackageAlias: "mm_" + sourcePackage.Name, 169 | HeaderTemplate: minimock.HeaderTemplate, 170 | BodyTemplate: minimock.BodyTemplate, 171 | HeaderVars: map[string]interface{}{ 172 | "GenerateInstruction": !opts.noGenerate, 173 | "Version": version, 174 | "PackageName": packageName, 175 | "GenerateGoRun": opts.goGenerateGoRun, 176 | }, 177 | Vars: map[string]interface{}{}, 178 | Funcs: helpers, 179 | } 180 | 181 | mockName := "" 182 | if len(opts.interfaces) == len(opts.mockNames) { 183 | mockName = opts.mockNames[i] 184 | } 185 | if err := processPackage(gopts, interfaces, in.WriteTo, opts.prefix, opts.suffix, mockName); err != nil { 186 | return err 187 | } 188 | } 189 | 190 | return nil 191 | } 192 | 193 | func processPackage(opts generator.Options, interfaces []types.InterfaceSpecification, writeTo, prefix, suffix, mockName string) (err error) { 194 | for _, iface := range interfaces { 195 | opts.InterfaceName = iface.InterfaceName 196 | 197 | params := []string{} 198 | paramsReferences := "" 199 | 200 | for _, param := range iface.InterfaceParams { 201 | names := strings.Join(param.ParamNames, ",") 202 | 203 | params = append(params, fmt.Sprintf("%s %s", names, param.ParamType)) 204 | if paramsReferences == "" { 205 | paramsReferences = names 206 | } else { 207 | paramsReferences = strings.Join([]string{paramsReferences, names}, ",") 208 | } 209 | } 210 | 211 | paramsString := strings.Join(params, ",") 212 | 213 | opts.OutputFile, err = destinationFile(iface.InterfaceName, writeTo, prefix, suffix) 214 | if err != nil { 215 | return errors.Wrapf(err, "failed to generate mock for %s", iface.InterfaceName) 216 | } 217 | 218 | opts.HeaderVars["OutputFile"] = filepath.Base(opts.OutputFile) 219 | 220 | opts.Vars["MockName"] = fmt.Sprintf("%sMock", opts.InterfaceName) 221 | if mockName != "" { 222 | opts.Vars["MockName"] = mockName 223 | } 224 | 225 | // Due to limitations of the generator, type params render is done by additional functions 226 | // params generates tokens for type param declarations, i.e. for declaring a generic function 227 | opts.Funcs["params"] = func() string { 228 | if paramsString != "" { 229 | return "[" + paramsString + "]" 230 | } 231 | return "" 232 | } 233 | 234 | // Due to limitations of the generator, type params render is done by additional functions 235 | // paramsRef generates cases when only a reference is needed, i.e. for instantiation 236 | opts.Funcs["paramsRef"] = func() string { 237 | if paramsReferences != "" { 238 | return "[" + paramsReferences + "]" 239 | } 240 | 241 | return "" 242 | } 243 | 244 | if err := generate(opts); err != nil { 245 | if strings.Contains(err.Error(), "interface has no methods") { 246 | continue 247 | } 248 | 249 | return err 250 | } 251 | 252 | fmt.Printf("minimock: %s\n", opts.OutputFile) 253 | } 254 | 255 | return nil 256 | } 257 | 258 | func isGoFile(path string) (bool, error) { 259 | stat, err := os.Stat(path) 260 | if err != nil { 261 | if !os.IsNotExist(err) { 262 | return false, err 263 | } 264 | 265 | dir := filepath.Dir(path) 266 | if stat, err = os.Stat(dir); err != nil { 267 | return false, err 268 | } 269 | 270 | if !stat.IsDir() { 271 | return false, errors.Errorf("not a directory: %s", dir) 272 | } 273 | 274 | return strings.HasSuffix(path, ".go"), nil 275 | } 276 | 277 | return strings.HasSuffix(path, ".go") && !stat.IsDir(), nil 278 | } 279 | 280 | func destinationFile(interfaceName, writeTo, prefix, suffix string) (string, error) { 281 | ok, err := isGoFile(writeTo) 282 | if err != nil { 283 | return "", err 284 | } 285 | 286 | var path string 287 | 288 | if ok { 289 | path = writeTo 290 | } else { 291 | path = filepath.Join(writeTo, prefix+minimock.CamelToSnake(interfaceName)+suffix) 292 | } 293 | 294 | if !strings.HasPrefix(path, "/") && !strings.HasPrefix(path, ".") { 295 | path = "./" + path 296 | } 297 | 298 | return path, nil 299 | } 300 | 301 | func generate(o generator.Options) (err error) { 302 | g, err := generator.NewGenerator(o) 303 | if err != nil { 304 | return err 305 | } 306 | 307 | buf := bytes.NewBuffer([]byte{}) 308 | 309 | if err = g.Generate(buf); err != nil { 310 | return errors.Wrap(err, "failed to generate mock") 311 | } 312 | 313 | return os.WriteFile(o.OutputFile, buf.Bytes(), 0644) 314 | } 315 | 316 | func match(s, pattern string) bool { 317 | return pattern == "*" || s == pattern 318 | } 319 | 320 | func usage(fs *flag.FlagSet, w io.Writer) { 321 | const usageTemplate = `Usage: {{bold "minimock"}} [{{bold "-i"}} source.interface] [{{bold "-o"}} output/dir/or/file.go] [{{bold "-g"}}] 322 | {{.}} 323 | Examples: 324 | 325 | Generate mocks for all interfaces that can be found in the current directory: 326 | {{bold "minimock"}} 327 | 328 | Generate mock for the io.Writer interface and put it into the "./buffer" package: 329 | {{bold "minimock"}} {{bold "-i"}} io.Writer {{bold "-o"}} ./buffer 330 | 331 | Generate mocks for the fmt.Stringer and all interfaces from the "io" package and put them into the "./buffer" package: 332 | {{bold "minimock"}} {{bold "-i"}} fmt.Stringer,io.* {{bold "-o"}} ./buffer 333 | 334 | For more information please visit https://github.com/gojuno/minimock 335 | ` 336 | 337 | t := template.Must(template.New("usage").Funcs(template.FuncMap{ 338 | "bold": func(s string) string { return "\033[1m" + s + "\033[0m" }, 339 | }).Parse(usageTemplate)) 340 | 341 | buf := bytes.NewBuffer([]byte{}) 342 | 343 | fs.SetOutput(buf) 344 | fs.PrintDefaults() 345 | 346 | if err := t.Execute(w, buf.String()); err != nil { 347 | panic(err) //something went completely wrong, i.e. OOM, closed pipe, etc 348 | } 349 | } 350 | 351 | func showVersion(w io.Writer) { 352 | const versionTemplate = `MiniMock version {{bold .Version}} 353 | Git commit: {{bold .Commit}} 354 | Build date: {{bold .BuildDate}} 355 | ` 356 | 357 | t := template.Must(template.New("version").Funcs(template.FuncMap{ 358 | "bold": func(s string) string { return "\033[1m" + s + "\033[0m" }, 359 | }).Parse(versionTemplate)) 360 | 361 | versionInfo := struct { 362 | Version string 363 | Commit string 364 | BuildDate string 365 | }{version, commit, buildDate} 366 | 367 | if err := t.Execute(w, versionInfo); err != nil { 368 | panic(err) //something went completely wrong, i.e. OOM, closed pipe, etc 369 | } 370 | } 371 | 372 | func processNames(names string, interfacesNum int, isInterfaceWildeCarded bool) ([]string, error) { 373 | if names == "" { 374 | return nil, nil 375 | } 376 | 377 | namesSplitted := strings.Split(names, ",") 378 | if len(namesSplitted) != 0 && len(namesSplitted) != interfacesNum { 379 | return nil, errors.Errorf("count of the source interfaces doesn't match the names count") 380 | } 381 | if len(namesSplitted) != 0 && isInterfaceWildeCarded { 382 | return nil, errors.Errorf("wildcards * can't be used with naming argument") 383 | } 384 | 385 | return namesSplitted, nil 386 | } 387 | 388 | var errInvalidArguments = errors.New("invalid arguments") 389 | 390 | func processArgs(args []string, stdout, stderr io.Writer) (*options, error) { 391 | var opts options 392 | 393 | fs := flag.NewFlagSet("", flag.ContinueOnError) 394 | 395 | fs.BoolVar(&opts.noGenerate, "g", false, "don't put go:generate instruction into the generated code") 396 | fs.BoolVar(&opts.goGenerateGoRun, "gr", false, `changes go:generate line from "//go:generate minimock args..." to "//go:generate go run github.com/gojuno/minimock/v3/cmd/minimock", 397 | useful while controlling minimock version with go mod`) 398 | fs.StringVar(&opts.suffix, "s", "_mock_test.go", "mock file suffix") 399 | fs.StringVar(&opts.prefix, "pr", "", "mock file prefix") 400 | input := fs.String("i", "*", "comma-separated names of the interfaces to mock, i.e fmt.Stringer,io.Reader\nuse io.* notation to generate mocks for all interfaces in the \"io\" package") 401 | output := fs.String("o", "", "comma-separated destination file names or packages to put the generated mocks in,\nby default the generated mock is placed in the source package directory") 402 | aliases := fs.String("n", "", "comma-separated mock names,\nby default the generated mock names append `Mock` to the given interface name") 403 | packageNames := fs.String("p", "", "comma-separated package names,\nby default the generated package names are taken from the destination directory names") 404 | help := fs.Bool("h", false, "show this help message") 405 | version := fs.Bool("version", false, "display version information and exit") 406 | 407 | fs.Usage = func() { usage(fs, stderr) } 408 | 409 | if err := fs.Parse(args); err != nil { 410 | return nil, errInvalidArguments 411 | } 412 | 413 | if *version { 414 | showVersion(stdout) 415 | return nil, nil 416 | } 417 | 418 | if *help { 419 | usage(fs, stdout) 420 | return nil, nil 421 | } 422 | 423 | interfaces := strings.Split(*input, ",") 424 | interfacesLen := len(interfaces) 425 | isWildecarded := strings.Contains(*input, "*") 426 | 427 | mockNames, err := processNames(*aliases, interfacesLen, isWildecarded) 428 | if err != nil { 429 | return nil, fmt.Errorf("processing -n flag arguments: %w", err) 430 | } 431 | opts.mockNames = mockNames 432 | 433 | parsedPackages, err := processNames(*packageNames, interfacesLen, isWildecarded) 434 | if err != nil { 435 | return nil, fmt.Errorf("processing -p flag arguments: %w", err) 436 | } 437 | opts.packageNames = parsedPackages 438 | 439 | var writeTo = make([]string, len(interfaces)) 440 | if *output != "" { 441 | //if only one output package specified 442 | if to := strings.Split(*output, ","); len(to) == 1 { 443 | for i := range writeTo { 444 | writeTo[i] = to[0] 445 | } 446 | } else { 447 | writeTo = to 448 | } 449 | } 450 | 451 | if len(writeTo) != len(interfaces) { 452 | return nil, errors.Errorf("count of the source interfaces doesn't match the output files count") 453 | } 454 | 455 | if err := checkDuplicateOutputFiles(writeTo); err != nil { 456 | return nil, err 457 | } 458 | 459 | for i, in := range interfaces { 460 | info, err := makeInterfaceInfo(in, writeTo[i]) 461 | if err != nil { 462 | return nil, err 463 | } 464 | 465 | opts.interfaces = append(opts.interfaces, *info) 466 | } 467 | 468 | return &opts, nil 469 | } 470 | 471 | // checkDuplicateOutputFiles finds first non-unique Go file 472 | func checkDuplicateOutputFiles(fileNames []string) error { 473 | for i := range fileNames { 474 | ok, err := isGoFile(fileNames[i]) 475 | if err != nil { 476 | return err 477 | } 478 | 479 | if !ok { 480 | continue 481 | } 482 | 483 | ipath, err := filepath.Abs(fileNames[i]) 484 | if err != nil { 485 | return err 486 | } 487 | 488 | for j := range fileNames { 489 | jpath, err := filepath.Abs(fileNames[j]) 490 | if err != nil { 491 | return err 492 | } 493 | 494 | if j != i && ipath == jpath { 495 | return errors.Errorf("duplicate output file name: %s", ipath) 496 | } 497 | } 498 | } 499 | 500 | return nil 501 | } 502 | 503 | func makeInterfaceInfo(typ, writeTo string) (*interfaceInfo, error) { 504 | info := interfaceInfo{WriteTo: writeTo} 505 | 506 | dot := strings.LastIndex(typ, ".") 507 | slash := strings.LastIndex(typ, "/") 508 | 509 | if slash > dot { 510 | return nil, errors.Errorf("invalid interface type: %s", typ) 511 | } 512 | 513 | if dot >= 0 { 514 | info.Type = typ[dot+1:] 515 | info.ImportPath = typ[:dot] 516 | } else { 517 | info.Type = typ 518 | info.ImportPath = "./" 519 | } 520 | 521 | return &info, nil 522 | } 523 | 524 | func die(format string, args ...interface{}) { 525 | if _, err := fmt.Fprintf(os.Stderr, "minimock: "+format+"\n", args...); err != nil { 526 | panic(err) 527 | } 528 | 529 | os.Exit(1) 530 | } 531 | --------------------------------------------------------------------------------