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 ```