{"id":15443782,"url":"https://github.com/petergtz/pegomock","last_synced_at":"2025-04-04T15:10:33.538Z","repository":{"id":8616604,"uuid":"58944928","full_name":"petergtz/pegomock","owner":"petergtz","description":" Pegomock is a powerful, yet simple mocking framework for the Go programming language","archived":false,"fork":false,"pushed_at":"2024-06-15T02:30:29.000Z","size":675,"stargazers_count":255,"open_issues_count":27,"forks_count":28,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-28T14:08:08.212Z","etag":null,"topics":["golang","mock","mocking-framework"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/petergtz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-05-16T15:44:43.000Z","updated_at":"2025-02-11T21:32:37.000Z","dependencies_parsed_at":"2025-02-15T13:12:06.326Z","dependency_job_id":"14fc3922-6ebe-4b0f-9775-2fb56ef3f901","html_url":"https://github.com/petergtz/pegomock","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petergtz%2Fpegomock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petergtz%2Fpegomock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petergtz%2Fpegomock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petergtz%2Fpegomock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/petergtz","download_url":"https://codeload.github.com/petergtz/pegomock/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247198463,"owners_count":20900080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","mock","mocking-framework"],"created_at":"2024-10-01T19:36:52.321Z","updated_at":"2025-04-04T15:10:33.490Z","avatar_url":"https://github.com/petergtz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"right\"\u003e\n\n[![Unit tests](https://github.com/petergtz/pegomock/actions/workflows/run-tests.yml/badge.svg)](https://github.com/petergtz/pegomock/actions/workflows/run-tests.yml)\n\n\u003c/div\u003e\n\n\u003cimg src=\"logo.svg\" height=\"150\" alt=\"logo\"\u003e\n\nPegoMock is a mocking framework for the [Go programming language](http://golang.org/). It integrates well with Go's built-in `testing` package, but can be used in other contexts too. It is based on [golang/mock](https://github.com/golang/mock), but uses a DSL closely related to [Mockito](http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html).\n\nInstalling Pegomock\n===================\n\nPegomock consists of a binary `pegomock` and a package. Install both via:\n\n```shell\ngo install github.com/petergtz/pegomock/v4/pegomock@latest\ngo get github.com/petergtz/pegomock/v4@latest\n```\n\nThis will download the package and install an executable `pegomock` in the directory named by the `$GOBIN` environment variable, which defaults to `$GOPATH/bin` or `$HOME/go/bin` if the `$GOPATH` environment variable is not set.\n\nThe `pegomock` binary is used to generate mocks and to watch over changes in interfaces. The package is used in your tests to create and verify mocks.\n\n\nSee also section [Tracking the pegomock tool in your project](#tracking-the-pegomock-tool-in-your-project) for a per-project control of the tool version.\n\nFor migration from Pegomock v1/v2 to v3, see [Migration notes in v3 release description](https://github.com/petergtz/pegomock/releases/tag/v3.0.0).\n\nFor migration from Pegomock v3 to v4, see [Migration notes in v4 release description](https://github.com/petergtz/pegomock/releases/tag/v4.0.0).\n\nGetting Started\n===============\n\nUsing Pegomock with Golang’s XUnit-style Tests\n----------------------------------------------\n\nThe preferred way is:\n\n```go\nimport (\n\t\"github.com/petergtz/pegomock/v4\"\n\t\"testing\"\n)\n\nfunc TestUsingMocks(t *testing.T) {\n\tmock := NewMockPhoneBook(pegomock.WithT(t))\n\n\t// use your mock here\n}\n```\n\n\nAlternatively, you can set a global fail handler within your test:\n\n```go\nfunc TestUsingMocks(t *testing.T) {\n\tpegomock.RegisterMockTestingT(t)\n\n\tmock := NewMockPhoneBook()\n\n\t// use your mock here\n}\n```\n**Note:** In this case, Pegomock uses a global (singleton) fail handler. This has the benefit that you don’t need to pass the fail handler down to each test, but does mean that you cannot run your XUnit style tests in parallel with Pegomock.\n\nIf you configure both a global fail handler and a specific one for your mock, the specific one overrides the global fail handler.\n\nUsing Pegomock with Ginkgo\n--------------------------\n\nWhen a Pegomock verification fails, it calls a `FailHandler`. This is a function that you must provide using `pegomock.RegisterMockFailHandler()`.\n\nIf you’re using [Ginkgo](http://onsi.github.io/ginkgo/), all you need to do is:\n\n```go\npegomock.RegisterMockFailHandler(ginkgo.Fail)\n```\n\nbefore you start your test suite.\n\n### Avoiding Ginkgo Naming Collision with `When` Function\n\nGinkgo introduced a new keyword in its DSL: `When`. This causes name collisions when dot-importing both Ginkgo and Pegomock. To avoid this, you can use a different dot-import for Pegomock which uses `Whenever` instead of `When`. Example:\n\n```go\npackage some_test\n\nimport (\n\t. \"github.com/onsi/ginkgo\"\n\t. \"github.com/petergtz/pegomock/v4/ginkgo_compatible\"\n)\n\nvar _ = Describe(\"Some function\", func() {\n\tWhen(\"certain condition\", func() {\n\t\tIt(\"succeeds\", func() {\n\t\t\tmock := NewMockPhoneBook()\n\t\t\tWhenever(mock.GetPhoneNumber(Eq(\"Tom\"))).ThenReturn(\"123-456-789\")\n\t\t})\n\t})\n})\n```\n\nGenerating Your First Mock and Using It\n---------------------------------------\n\nLet's assume you have:\n\n```go\ntype Display interface {\n\tShow(text string)\n}\n```\n\nThe simplest way is to call `pegomock` from within your go package specifying the interface by its name:\n\n```\ncd path/to/package\npegomock generate Display\n```\n\nThis will generate a `mock_display_test.go` file which you can now use in your tests:\n\n```go\n// creating mock\ndisplay := NewMockDisplay()\n\n// using the mock\ndisplay.Show(\"Hello World!\")\n\n// verifying\ndisplay.VerifyWasCalledOnce().Show(\"Hello World!\")\n```\n\nWhy yet Another Mocking Framework for Go?\n=========================================\n\nI've looked at some of the other frameworks, but found none of them satisfying:\n- [GoMock](https://github.com/golang/mock) seemed overly complicated when setting up mocks and verifying them. The command line interface is also not quite intuitive. That said, Pegomock is based on the GoMock, reusing mostly the mockgen code.\n- [Counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) uses a DSL that I didn't find expressive enough. It often seems to need more lines of code too. In one of its samples, it uses e.g.:\n\n\t```go\n\tfake.DoThings(\"stuff\", 5)\n\tExpect(fake.DoThingsCallCount()).To(Equal(1))\n\n\tstr, num := fake.DoThingsArgsForCall(0)\n\tExpect(str).To(Equal(\"stuff\"))\n\tExpect(num).To(Equal(uint64(5)))\n\t```\n\n\tIn Pegomock, this can be written as simple as:\n\n\t```go\n\tfake.DoThings(\"stuff\", 5)\n\tfake.VerifyWasCalledOnce().DoThings(\"stuff\", 5)\n\t```\n\nIn addition, Pegomock provides a \"watch\" command similar to [Ginkgo](http://onsi.github.io/ginkgo/), which constantly watches over changes in an interface and updates its mocks. It gives the framework a much more dynamic feel, similar to mocking frameworks in Ruby or Java.\n\nUsing Mocks In Your Tests\n=========================\n\nVerifying Behavior\n------------------\n\nInterface:\n\n```go\ntype Display interface {\n\tShow(text string)\n}\n```\n\nTest:\n\n```go\n// creating mock:\ndisplay := NewMockDisplay()\n\n// using the mock:\ndisplay.Show(\"Hello World!\")\n\n// verifying:\ndisplay.VerifyWasCalledOnce().Show(\"Hello World!\")\n```\n\nStubbing\n--------\n\nInterface:\n\n```go\ntype PhoneBook interface {\n\tGetPhoneNumber(name string) string\n}\n```\n\nTest:\n\n```go\n// creating the mock\nphoneBook := NewMockPhoneBook()\n\n// stubbing:\nWhen(phoneBook.GetPhoneNumber(\"Tom\")).ThenReturn(\"345-123-789\")\nWhen(phoneBook.GetPhoneNumber(\"Invalid\")).ThenPanic(\"Invalid Name\")\n\n// prints \"345-123-789\":\nfmt.Println(phoneBook.GetPhoneNumber(\"Tom\"))\n\n// panics:\nfmt.Println(phoneBook.GetPhoneNumber(\"Invalid\"))\n\n// prints \"\", because GetPhoneNumber(\"Dan\") was not stubbed\nfmt.Println(phoneBook.GetPhoneNumber(\"Dan\"))\n\n// Although it is possible to verify a stubbed invocation, usually it's redundant\n// If your code cares what GetPhoneNumber(\"Tom\") returns, then something else breaks (often even before a verification gets executed).\n// If your code doesn't care what GetPhoneNumber(\"Tom\") returns, then it should not be stubbed.\n\n// Not convinced? See http://monkeyisland.pl/2008/04/26/asking-and-telling.\nphoneBook.VerifyWasCalledOnce().GetPhoneNumber(\"Tom\")\n```\n\n-\tBy default, for all methods that return a value, a mock will return zero values.\n-\tOnce stubbed, the method will always return a stubbed value, regardless of how many times it is called.\n- `ThenReturn` supports chaining, i.e. `ThenReturn(...).ThenReturn(...)` etc. The mock will return the values in the same order the chaining was done. The values from the last `ThenReturn` will be returned indefinitely when the number of call exceeds the `ThenReturn`s.\n\nStubbing Functions That Have no Return Value\n--------------------------------------------\n\nStubbing functions that have no return value requires a slightly different approach, because such functions cannot be passed directly to another function. However, we can wrap them in an anonymous function:\n\n```go\n// creating mock:\ndisplay := NewMockDisplay()\n\n// stubbing\nWhen(func() { display.Show(\"Hello World!\") }).ThenPanic(\"Panicking\")\n\n// panics:\ndisplay.Show(\"Hello World!\")\n```\n\nArgument Matchers\n-----------------\n\nPegomock provides matchers for stubbing and verification.\n\nVerification:\n\n```go\ndisplay := NewMockDisplay()\n\n// Calling mock\ndisplay.Show(\"Hello again!\")\n\n// Verification:\ndisplay.VerifyWasCalledOnce().Show(AnyString())\n```\n\nStubbing:\n\n```go\nphoneBook := NewMockPhoneBook()\n\n// Stubbing:\nWhen(phoneBook.GetPhoneNumber(AnyString())).ThenReturn(\"123-456-789\")\n\n// Prints \"123-456-789\":\nfmt.Println(phoneBook.GetPhoneNumber(\"Dan\"))\n// Also prints \"123-456-789\":\nfmt.Println(phoneBook.GetPhoneNumber(\"Tom\"))\n```\n\n**Important**: When you use argument matchers, you must always use them for all arguments:\n\n```go\n// Incorrect, panics:\nWhen(contactList.getContactByFullName(\"Dan\", AnyString())).thenReturn(Contact{...})\n// Correct:\nWhen(contactList.getContactByFullName(Eq(\"Dan\"), AnyString())).thenReturn(Contact{...})\n```\n\nMatching custom types:\n\n```go\ntype SearchQuery struct { ... }\n\nWhen(contactList.searchContacts(Eq(SearchQuery{...}))).thenReturn([]Contact{...})\n\nWhen(contactList.searchContacts(Any[SearchQuery]())).thenReturn([]Contact{...})\n```\n\n\nVerifying the Number of Invocations\n-----------------------------------\n\n```go\ndisplay := NewMockDisplay()\n\n// Calling mock\ndisplay.Show(\"Hello\")\ndisplay.Show(\"Hello, again\")\ndisplay.Show(\"And again\")\n\n// Verification:\ndisplay.VerifyWasCalled(Times(3)).Show(AnyString())\n// or:\ndisplay.VerifyWasCalled(AtLeast(3)).Show(AnyString())\n// or:\ndisplay.VerifyWasCalled(Never()).Show(\"This one was never called\")\n```\n\nVerifying in Order\n------------------\n\n```go\ndisplay1 := NewMockDisplay()\ndisplay2 := NewMockDisplay()\n\n// Calling mocks\ndisplay1.Show(\"One\")\ndisplay1.Show(\"Two\")\ndisplay2.Show(\"Another two\")\ndisplay1.Show(\"Three\")\n\n// Verification:\ninOrderContext := new(InOrderContext)\ndisplay1.VerifyWasCalledInOrder(Once(), inOrderContext).Show(\"One\")\ndisplay2.VerifyWasCalledInOrder(Once(), inOrderContext).Show(\"Another two\")\ndisplay1.VerifyWasCalledInOrder(Once(), inOrderContext).Show(\"Three\")\n```\n\nNote that it's not necessary to verify the call for `display.Show(\"Two\")` if that one is not of any interested. An `InOrderContext` only verifies that the verifications that are done, are in order.\n\nStubbing with Callbacks\n------------------------\n\n```go\nphoneBook := NewMockPhoneBook()\n\n// Stubbing:\nWhen(phoneBook.GetPhoneNumber(AnyString())).Then(func(params []Param) ReturnValues {\n\treturn []ReturnValue{fmt.Sprintf(\"1-800-CALL-%v\", strings.ToUpper(params[0]))}\n},\n\n\n// Prints \"1-800-CALL-DAN\":\nfmt.Println(phoneBook.GetPhoneNumber(\"Dan\"))\n// Prints \"1-800-CALL-TOM\":\nfmt.Println(phoneBook.GetPhoneNumber(\"Tom\"))\n```\n\n\nVerifying with Argument Capture\n--------------------------------\n\nIn some cases it can be useful to capture the arguments from mock invocations and assert on them separately. This method is only recommended if the techniques using matchers are not sufficient.\n\n```go\ndisplay := NewMockDisplay()\n\n// Calling mock\ndisplay.Show(\"Hello\")\ndisplay.Show(\"Hello, again\")\ndisplay.Show(\"And again\")\n\n// Verification and getting captured arguments\ntext := display.VerifyWasCalled(AtLeast(1)).Show(AnyString()).GetCapturedArguments()\n\n// Captured arguments are from last invocation\nExpect(text).To(Equal(\"And again\"))\n```\n\nYou can also get all captured arguments:\n\n```go\n// Verification and getting all captured arguments\ntexts := display.VerifyWasCalled(AtLeast(1)).Show(AnyString()).GetAllCapturedArguments()\n\n// Captured arguments are a slice\nExpect(texts).To(ConsistOf(\"Hello\", \"Hello, again\", \"And again\"))\n```\n\nVerifying with Asynchronous Mock Invocations\n--------------------------------------------\n\nWhen the code exercising the mock is run as part of a Goroutine, it's necessary to verify in a polling fashion until a timeout kicks in. `VerifyWasCalledEventually` can help here:\n```go\ndisplay := NewMockDisplay()\n\ngo func() {\n\tdoSomething()\n\tdisplay.Show(\"Hello\")\n}()\n\ndisplay.VerifyWasCalledEventually(Once(), 2*time.Second).Show(\"Hello\")\n```\n\n\nThe Pegomock CLI\n================\n\nInstallation\n------------\n\nInstall it via:\n\n```shell\ngo install github.com/petergtz/pegomock/v4/pegomock@latest\n```\n\nTracking the pegomock tool in your project\n------------------------------------------\n\nGo modules allow to pin not only a package but also a tool (that is, an executable). The steps are:\n\n1. Use a file named `tools.go` with contents similar to this:\n```go\n// +build tools\n\n// This file will never be compiled (see the build constraint above); it is\n// used to record dependencies on build tools with the Go modules machinery.\n// See https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md\n\npackage tools\n\nimport (\n\t_ \"github.com/petergtz/pegomock/v4/pegomock\"\n)\n```\n2. Set `$GOBIN` to a `bin` directory relative to your repo (this defines where tool dependencies will be installed).\n2. Install the tool with `go install`:\n```console\n$ cd /path/to/myproject\n$ export GOBIN=$PWD/bin\n$ go install github.com/petergtz/pegomock/v4/pegomock\n```\n3. Use that `$GOBIN` when invoking `pegomock` for that project:\n```console\n$ $GOBIN/pegomock ...\n```\nor\n```console\n$ export PATH=$GOBIN:$PATH\n$ pegomock ...\n```\n\nSee [Tools as dependencies] for details.\n\n[Tools as dependencies]: https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md\n\nGenerating Mocks\n----------------\n\nTo generate mocks, invoke Pegomock like this:\n\n```shell \npegomock generate [\u003cflags\u003e] [\u003cpackagepath\u003e] \u003cinterfacename\u003e\n```\n\nFlags can be any of the following:\n\n-\t`--output,-o`: Output file; defaults to mock_\u003cinterface\u003e_test.go.\n\n-\t`--package`: Package of the generated code; defaults to the package from which pegomock was executed suffixed with _test\n\nFor more flags, run:\n\n```shell\npegomock --help\n```\n\nGenerating Mocks with `--use-experimental-model-gen`\n----------------------------------------------------\n\nThere are a number of shortcomings in the current reflection-based implementation.\nTo overcome these, there is now an option to use a new, experimental implementation that is based on [golang.org/x/tools/go/loader](https://godoc.org/golang.org/x/tools/go/loader).\nTo use it when generating your mocks, invoke `pegomock` like this:\n\n```shell\npegomock generate --use-experimental-model-gen [\u003cflags\u003e] [\u003cpackagepath\u003e] \u003cinterfacename\u003e\n```\n\nWhat are the benefits?\n- The current default uses the [reflect](https://golang.org/pkg/reflect/) package to introspect the interface for which a mock should be generated. But reflection cannot determine method parameter names, only types. This forces the generator to generate them based on a pattern. In a code editor with code assistence, those pattern-based names (such as `_param0`, `_param1`) are non-descriptive and provide less help while writing code. The new implementation properly parses the source (including *all* dependent packages) and subsequently uses the same names as used in the interface definition.\n- With the current default you cannot generate an interface that lives in the `main` package. It's due to the way this implementation works: it imports the interface's package into temporarily generated code that gets compiled on the fly. This compilation fails, because there are now two `main` functions.\n- The new implementation is simpler and will probably become the default in the future, because it will be easier to maintain.\n\nWhat are the drawbacks?\n- There is only one drawback: maturity. The new implementation is not complete yet, and also might have some bugs that still need to be fixed.\n\nUsers of Pegomock are encouraged to use this new option and report any problems by [opening an issue](https://github.com/petergtz/pegomock/issues/new). Help to stabilize it is greatly appreciated.\n\nGenerating mocks with `go generate`\n----------------------------------\n\n`pegomock` can be used with `go generate`. Simply add the directive to your source file.\n\nHere's an example for a Display interface used by a calculator program:\n\n```go\n// package/path/to/display/display.go\n\npackage display\n\ntype Display interface {\n\tShow(text string)\n}\n```\n\n```go\n// package/path/to/calculator/calculator_test.go\n\npackage calculator_test\n\n//go:generate pegomock generate package/path/to/display Display\n\n// Use generated mock\nmockDisplay := NewMockDisplay()\n...\n```\n\nGenerating it:\n```shell\ncd package/path/to/calculator\ngo generate\n```\n\n**Note:** While you could add the directive adjacent to the interface definition, the author's opinion is that this violates clean dependency management and would pollute the package of the interface.\nIt's better to generate the mock in the same package, where it is used (if this coincides with the interface package, that's fine). That way, not only stays the interface's package clean, the tests also don't need to prefix the mock with a package, or use a dot-import.\n\nContinuously Generating Mocks\n-----------------------------\n\nThe `watch` command lets Pegomock generate mocks continuously on every change to an interface:\n\n```shell\npegomock watch\n```\n\nFor this, Pegomock expects an `interfaces_to_mock` file in the package directory where the mocks should be generated. In fact, `pegomock watch` will create it for you if it doesn't exist yet. The contents of the file are similar to the ones of the `generate` command:\n\n```\n# Any line starting with a # is treated as comment.\n\n# interface name without package specifies an Interface in the current package:\nPhoneBook\n\n # generates a mock for SomeInterfacetaken from mypackage:\npath/to/my/mypackage SomeInterface\n\n# you can also specify a Go file:\ndisplay.go\n\n# and use most of the flags from the \"generate\" command\n--output my_special_output.go MyInterface\n```\n\nFlags can be:\n\n- `--recursive,-r`: Recursively watch sub-directories as well.\n\nRemoving Generated Mocks\n-----------------------------\n\nSometimes it can be useful to systematically remove all mocks and matcher files generated by Pegomock. For this purpose, there is the `remove` command. By simply calling it from the current directory\n```shell\npegomock remove\n```\nit will remove all Pegomock-generated files in the current directory. It supports additional flags, such as `--recursive` to recursively remove all Pegomock-generated files in sub-directories as well. To see all possible options, run:\n```shell\npegomock remove --help\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetergtz%2Fpegomock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpetergtz%2Fpegomock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetergtz%2Fpegomock/lists"}