github.com/petergtz/pegomock@v2.9.1-0.20230424204322-eb0e044013df+incompatible/README.md (about)

     1  
     2  <img src="logo.svg" height="150" alt="logo">
     3  
     4  PegoMock 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).
     5  
     6  Getting Pegomock
     7  ================
     8  
     9  Just `go get` it:
    10  
    11  ```
    12  go get github.com/petergtz/pegomock/...
    13  ```
    14  
    15  This will download the package and install an executable `pegomock` in your `$GOPATH/bin`.
    16  
    17  See 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.
    18  
    19  Getting Started
    20  ===============
    21  
    22  Using Pegomock with Golang’s XUnit-style Tests
    23  ----------------------------------------------
    24  
    25  The preferred way is:
    26  
    27  ```go
    28  import (
    29  	"github.com/petergtz/pegomock"
    30  	"testing"
    31  )
    32  
    33  func TestUsingMocks(t *testing.T) {
    34  	mock := NewMockPhoneBook(pegomock.WithT(t))
    35  
    36  	// use your mock here
    37  }
    38  ```
    39  
    40  
    41  Alternatively, you can set a global fail handler within your test:
    42  
    43  ```go
    44  func TestUsingMocks(t *testing.T) {
    45  	pegomock.RegisterMockTestingT(t)
    46  
    47  	mock := NewMockPhoneBook()
    48  
    49  	// use your mock here
    50  }
    51  ```
    52  **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.
    53  
    54  If you configure both a global fail handler and a specific one for your mock, the specific one overrides the global fail handler.
    55  
    56  Using Pegomock with Ginkgo
    57  --------------------------
    58  
    59  When a Pegomock verification fails, it calls a `FailHandler`. This is a function that you must provide using `pegomock.RegisterMockFailHandler()`.
    60  
    61  If you’re using [Ginkgo](http://onsi.github.io/ginkgo/), all you need to do is:
    62  
    63  ```go
    64  pegomock.RegisterMockFailHandler(ginkgo.Fail)
    65  ```
    66  
    67  before you start your test suite.
    68  
    69  ### Avoiding Ginkgo Naming Collision with `When` Function
    70  
    71  Ginkgo 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:
    72  
    73  ```go
    74  package some_test
    75  
    76  import (
    77  	. "github.com/onsi/ginkgo"
    78  	. "github.com/petergtz/pegomock/ginkgo_compatible"
    79  )
    80  
    81  var _ = Describe("Some function", func() {
    82  	When("certain condition", func() {
    83  		It("succeeds", func() {
    84  			mock := NewMockPhoneBook()
    85  			Whenever(mock.GetPhoneNumber(EqString("Tom"))).ThenReturn("123-456-789")
    86  		})
    87  	})
    88  })
    89  ```
    90  
    91  Generating Your First Mock and Using It
    92  ---------------------------------------
    93  
    94  Let's assume you have:
    95  
    96  ```go
    97  type Display interface {
    98  	Show(text string)
    99  }
   100  ```
   101  
   102  The simplest way is to call `pegomock` from within your go package specifying the interface by its name:
   103  
   104  ```
   105  cd path/to/package
   106  pegomock generate Display
   107  ```
   108  
   109  This will generate a `mock_display_test.go` file which you can now use in your tests:
   110  
   111  ```go
   112  // creating mock
   113  display := NewMockDisplay()
   114  
   115  // using the mock
   116  display.Show("Hello World!")
   117  
   118  // verifying
   119  display.VerifyWasCalledOnce().Show("Hello World!")
   120  ```
   121  
   122  Why yet Another Mocking Framework for Go?
   123  =========================================
   124  
   125  I've looked at some of the other frameworks, but found none of them satisfying:
   126  - [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.
   127  - [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.:
   128  
   129  	```go
   130  	fake.DoThings("stuff", 5)
   131  	Expect(fake.DoThingsCallCount()).To(Equal(1))
   132  
   133  	str, num := fake.DoThingsArgsForCall(0)
   134  	Expect(str).To(Equal("stuff"))
   135  	Expect(num).To(Equal(uint64(5)))
   136  	```
   137  
   138  	In Pegomock, this can be written as simple as:
   139  
   140  	```go
   141  	fake.DoThings("stuff", 5)
   142  	fake.VerifyWasCalledOnce().DoThings("stuff", 5)
   143  	```
   144  - [Hel](https://github.com/nelsam/hel) uses a new and interesting approach to setting up and verifying mocks. However, I wonder how flexible it actually is. E.g. how about providing a callback function when stubbing? Can this be modeled with its current approach using channels?
   145  
   146  In 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.
   147  
   148  Using Mocks In Your Tests
   149  =========================
   150  
   151  Verifying Behavior
   152  ------------------
   153  
   154  Interface:
   155  
   156  ```go
   157  type Display interface {
   158  	Show(text string)
   159  }
   160  ```
   161  
   162  Test:
   163  
   164  ```go
   165  // creating mock:
   166  display := NewMockDisplay()
   167  
   168  // using the mock:
   169  display.Show("Hello World!")
   170  
   171  // verifying:
   172  display.VerifyWasCalledOnce().Show("Hello World!")
   173  ```
   174  
   175  Stubbing
   176  --------
   177  
   178  Interface:
   179  
   180  ```go
   181  type PhoneBook interface {
   182  	GetPhoneNumber(name string) string
   183  }
   184  ```
   185  
   186  Test:
   187  
   188  ```go
   189  // creating the mock
   190  phoneBook := NewMockPhoneBook()
   191  
   192  // stubbing:
   193  When(phoneBook.GetPhoneNumber("Tom")).ThenReturn("345-123-789")
   194  When(phoneBook.GetPhoneNumber("Invalid")).ThenPanic("Invalid Name")
   195  
   196  // prints "345-123-789":
   197  fmt.Println(phoneBook.GetPhoneNumber("Tom"))
   198  
   199  // panics:
   200  fmt.Println(phoneBook.GetPhoneNumber("Invalid"))
   201  
   202  // prints "", because GetPhoneNumber("Dan") was not stubbed
   203  fmt.Println(phoneBook.GetPhoneNumber("Dan"))
   204  
   205  // Although it is possible to verify a stubbed invocation, usually it's redundant
   206  // If your code cares what GetPhoneNumber("Tom") returns, then something else breaks (often even before a verification gets executed).
   207  // If your code doesn't care what GetPhoneNumber("Tom") returns, then it should not be stubbed.
   208  
   209  // Not convinced? See http://monkeyisland.pl/2008/04/26/asking-and-telling.
   210  phoneBook.VerifyWasCalledOnce().GetPhoneNumber("Tom")
   211  ```
   212  
   213  -	By default, for all methods that return a value, a mock will return zero values.
   214  -	Once stubbed, the method will always return a stubbed value, regardless of how many times it is called.
   215  - `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.
   216  
   217  Stubbing Functions That Have no Return Value
   218  --------------------------------------------
   219  
   220  Stubbing 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:
   221  
   222  ```go
   223  // creating mock:
   224  display := NewMockDisplay()
   225  
   226  // stubbing
   227  When(func() { display.Show("Hello World!") }).ThenPanic("Panicking")
   228  
   229  // panics:
   230  display.Show("Hello World!")
   231  ```
   232  
   233  Argument Matchers
   234  -----------------
   235  
   236  Pegomock provides matchers for stubbing and verification.
   237  
   238  Verification:
   239  
   240  ```go
   241  display := NewMockDisplay()
   242  
   243  // Calling mock
   244  display.Show("Hello again!")
   245  
   246  // Verification:
   247  display.VerifyWasCalledOnce().Show(AnyString())
   248  ```
   249  
   250  Stubbing:
   251  
   252  ```go
   253  phoneBook := NewMockPhoneBook()
   254  
   255  // Stubbing:
   256  When(phoneBook.GetPhoneNumber(AnyString())).ThenReturn("123-456-789")
   257  
   258  // Prints "123-456-789":
   259  fmt.Println(phoneBook.GetPhoneNumber("Dan"))
   260  // Also prints "123-456-789":
   261  fmt.Println(phoneBook.GetPhoneNumber("Tom"))
   262  ```
   263  
   264  **Important**: When you use argument matchers, you must always use them for all arguments:
   265  
   266  ```go
   267  // Incorrect, panics:
   268  When(contactList.getContactByFullName("Dan", AnyString())).thenReturn(Contact{...})
   269  // Correct:
   270  When(contactList.getContactByFullName(EqString("Dan"), AnyString())).thenReturn(Contact{...})
   271  ```
   272  
   273  ### Writing Your Own Argument Matchers
   274  
   275  **Important:** `Eq...`, `NotEq...`, `Any...` and `...That` matchers for types used in mock methods,
   276  can now be _auto-generated_ while generating the mock. The `...That` argument matcher accepts an
   277  argument implementing the `pegomock.ArgumentMatcher` interface and allows you to write and use custom
   278  matcher logic without having to create a new argument matcher method for each type you want to match.
   279  
   280  So writing your own argument matchers is not necessary for most use cases. See section
   281  [The Pegomock CLI](#generating-mocks) for more information.
   282  
   283  If you are not using the option to generate matchers you can write your own for non-basic types.
   284  E.g. if you have a `struct MyType`, you can write an _Equals_ and _Any_ matcher like this:
   285  ```go
   286  func EqMyType(value MyType) MyType {
   287  	RegisterMatcher(&EqMatcher{Value: value})
   288  	return MyType{}
   289  }
   290  
   291  func AnyMyType() MyType {
   292  	RegisterMatcher(NewAnyMatcher(reflect.TypeOf(MyType{})))
   293  	return MyType{}
   294  }
   295  ```
   296  
   297  
   298  Verifying the Number of Invocations
   299  -----------------------------------
   300  
   301  ```go
   302  display := NewMockDisplay()
   303  
   304  // Calling mock
   305  display.Show("Hello")
   306  display.Show("Hello, again")
   307  display.Show("And again")
   308  
   309  // Verification:
   310  display.VerifyWasCalled(Times(3)).Show(AnyString())
   311  // or:
   312  display.VerifyWasCalled(AtLeast(3)).Show(AnyString())
   313  // or:
   314  display.VerifyWasCalled(Never()).Show("This one was never called")
   315  ```
   316  
   317  Verifying in Order
   318  ------------------
   319  
   320  ```go
   321  display1 := NewMockDisplay()
   322  display2 := NewMockDisplay()
   323  
   324  // Calling mocks
   325  display1.Show("One")
   326  display1.Show("Two")
   327  display2.Show("Another two")
   328  display1.Show("Three")
   329  
   330  // Verification:
   331  inOrderContext := new(InOrderContext)
   332  display1.VerifyWasCalledInOrder(Once(), inOrderContext).Show("One")
   333  display2.VerifyWasCalledInOrder(Once(), inOrderContext).Show("Another two")
   334  display1.VerifyWasCalledInOrder(Once(), inOrderContext).Show("Three")
   335  ```
   336  
   337  Note 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.
   338  
   339  Stubbing with Callbacks
   340  ------------------------
   341  
   342  ```go
   343  phoneBook := NewMockPhoneBook()
   344  
   345  // Stubbing:
   346  When(phoneBook.GetPhoneNumber(AnyString())).Then(func(params []Param) ReturnValues {
   347  	return []ReturnValue{fmt.Sprintf("1-800-CALL-%v", strings.ToUpper(params[0]))}
   348  },
   349  
   350  
   351  // Prints "1-800-CALL-DAN":
   352  fmt.Println(phoneBook.GetPhoneNumber("Dan"))
   353  // Prints "1-800-CALL-TOM":
   354  fmt.Println(phoneBook.GetPhoneNumber("Tom"))
   355  ```
   356  
   357  
   358  Verifying with Argument Capture
   359  --------------------------------
   360  
   361  In 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.
   362  
   363  ```go
   364  display := NewMockDisplay()
   365  
   366  // Calling mock
   367  display.Show("Hello")
   368  display.Show("Hello, again")
   369  display.Show("And again")
   370  
   371  // Verification and getting captured arguments
   372  text := display.VerifyWasCalled(AtLeast(1)).Show(AnyString()).GetCapturedArguments()
   373  
   374  // Captured arguments are from last invocation
   375  Expect(text).To(Equal("And again"))
   376  ```
   377  
   378  You can also get all captured arguments:
   379  
   380  ```go
   381  // Verification and getting all captured arguments
   382  texts := display.VerifyWasCalled(AtLeast(1)).Show(AnyString()).GetAllCapturedArguments()
   383  
   384  // Captured arguments are a slice
   385  Expect(texts).To(ConsistOf("Hello", "Hello, again", "And again"))
   386  ```
   387  
   388  Verifying with Asynchronous Mock Invocations
   389  --------------------------------------------
   390  
   391  When 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:
   392  ```go
   393  display := NewMockDisplay()
   394  
   395  go func() {
   396  	doSomething()
   397  	display.Show("Hello")
   398  }()
   399  
   400  display.VerifyWasCalledEventually(Once(), 2*time.Second).Show("Hello")
   401  ```
   402  
   403  
   404  The Pegomock CLI
   405  ================
   406  
   407  Installation
   408  ------------
   409  
   410  Install it via:
   411  
   412  ```
   413  go install github.com/petergtz/pegomock/pegomock
   414  ```
   415  
   416  Tracking the pegomock tool in your project
   417  ------------------------------------------
   418  
   419  Go modules allow to pin not only a package but also a tool (that is, an executable). The steps are:
   420  
   421  1. Use a file named `tools.go` with contents similar to this:
   422  ```go
   423  // +build tools
   424  
   425  // This file will never be compiled (see the build constraint above); it is
   426  // used to record dependencies on build tools with the Go modules machinery.
   427  // See https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md
   428  
   429  package tools
   430  
   431  import (
   432  	_ "github.com/petergtz/pegomock/pegomock"
   433  )
   434  ```
   435  2. Set `$GOBIN` to a `bin` directory relative to your repo (this defines where tool dependencies will be installed).
   436  2. Install the tool with `go install`:
   437  ```console
   438  $ cd /path/to/myproject
   439  $ export GOBIN=$PWD/bin
   440  $ go install github.com/petergtz/pegomock/pegomock
   441  ```
   442  3. Use that `$GOBIN` when invoking `pegomock` for that project:
   443  ```console
   444  $ $GOBIN/pegomock ...
   445  ```
   446  or
   447  ```console
   448  $ export PATH=$GOBIN:$PATH
   449  $ pegomock ...
   450  ```
   451  
   452  See [Tools as dependencies] for details.
   453  
   454  [Tools as dependencies]: https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md
   455  
   456  Generating Mocks
   457  ----------------
   458  
   459  Pegomock can generate mocks in two different ways:
   460  
   461  1.	by parsing source code Go files
   462  
   463  	```
   464  	pegomock generate [<flags>] <gofile>
   465  	```
   466  
   467  2.	by building a Go package and using reflection
   468  
   469  	```
   470  	pegomock generate [<flags>] [<packagepath>] <interfacename>
   471  	```
   472  
   473  Flags can be any of the following:
   474  
   475  -	`--output,-o`: Output file; defaults to mock_<interface>_test.go.
   476  
   477  -	`--package`: Package of the generated code; defaults to the package from which pegomock was executed suffixed with _test
   478  
   479  - `--generate-matchers,-m`: This will auto-generate argument matchers and place them in a `matchers` directory alongside the mock source code itself.
   480  
   481  For more flags, run:
   482  
   483  ```
   484  pegomock --help
   485  ```
   486  
   487  Generating Mocks with `--use-experimental-model-gen`
   488  ----------------------------------------------------
   489  
   490  There are a number of shortcomings in the current reflection-based implementation.
   491  To 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).
   492  To use it when generating your mocks, invoke `pegomock` like this:
   493  
   494  ```
   495  pegomock generate --use-experimental-model-gen [<flags>] [<packagepath>] <interfacename>
   496  ```
   497  
   498  What are the benefits?
   499  - 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.
   500  - 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.
   501  - The new implementation is simpler and will probably become the default in the future, because it will be easier to maintain.
   502  
   503  What are the drawbacks?
   504  - 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.
   505  
   506  Users 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.
   507  
   508  Generating mocks with `go generate`
   509  ----------------------------------
   510  
   511  `pegomock` can be used with `go generate`. Simply add the directive to your source file.
   512  
   513  Here's an example for a Display interface used by a calculator program:
   514  
   515  ```go
   516  // package/path/to/display/display.go
   517  
   518  package display
   519  
   520  type Display interface {
   521  	Show(text string)
   522  }
   523  ```
   524  
   525  ```go
   526  // package/path/to/calculator/calculator_test.go
   527  
   528  package calculator_test
   529  
   530  //go:generate pegomock generate package/path/to/display Display
   531  
   532  // Use generated mock
   533  mockDisplay := NewMockDisplay()
   534  ...
   535  ```
   536  
   537  Generating it:
   538  ```sh
   539  cd package/path/to/calculator
   540  go generate
   541  ```
   542  
   543  **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.
   544  It'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.
   545  
   546  Continuously Generating Mocks
   547  -----------------------------
   548  
   549  The `watch` command lets Pegomock generate mocks continuously on every change to an interface:
   550  
   551  ```
   552  pegomock watch
   553  ```
   554  
   555  For 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:
   556  
   557  ```
   558  # Any line starting with a # is treated as comment.
   559  
   560  # interface name without package specifies an Interface in the current package:
   561  PhoneBook
   562  
   563   # generates a mock for SomeInterfacetaken from mypackage:
   564  path/to/my/mypackage SomeInterface
   565  
   566  # you can also specify a Go file:
   567  display.go
   568  
   569  # and use most of the flags from the "generate" command
   570  --output my_special_output.go MyInterface
   571  ```
   572  
   573  Flags can be:
   574  
   575  - `--recursive,-r`: Recursively watch sub-directories as well.
   576  
   577  Removing Generated Mocks
   578  -----------------------------
   579  
   580  Sometimes 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
   581  ```
   582  pegomock remove
   583  ```
   584  it 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:
   585  ```
   586  pegomock remove --help
   587  ```