github.com/joomcode/pegomock@v2.9.2-0.20220414140958-14f53b6b2a6c+incompatible/pegomock/main_test.go (about) 1 // Copyright 2016 Peter Goetz 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main_test 16 17 import ( 18 "bytes" 19 "go/build" 20 "os" 21 "path/filepath" 22 "strings" 23 24 kingpin "gopkg.in/alecthomas/kingpin.v2" 25 26 . "github.com/onsi/ginkgo" 27 . "github.com/onsi/gomega" 28 main "github.com/petergtz/pegomock/pegomock" 29 30 . "github.com/petergtz/pegomock/pegomock/testutil" 31 32 "testing" 33 ) 34 35 var ( 36 joinPath = filepath.Join 37 ) 38 39 func TestPegomock(t *testing.T) { 40 RegisterFailHandler(Fail) 41 RunSpecs(t, "CLI Suite") 42 } 43 44 var ( 45 _ = describeCLIWithGoModulesEnabled(false) 46 _ = describeCLIWithGoModulesEnabled(true) 47 ) 48 49 func describeCLIWithGoModulesEnabled(useGoModules bool) interface{} { 50 mode := "with GOPATH" 51 if useGoModules { 52 mode = "with Go modules" 53 } 54 55 Describe("CLI "+mode, func() { 56 57 var ( 58 packageDir, subPackageDir, vendorPackageDir string 59 app *kingpin.Application 60 origWorkingDir string 61 done chan bool = make(chan bool) 62 ) 63 64 BeforeEach(func() { 65 if !useGoModules { 66 packageDir = joinPath(build.Default.GOPATH, "src", "pegomocktest") 67 } else { 68 tmpDir, e := filepath.EvalSymlinks(os.TempDir()) 69 Expect(e).NotTo(HaveOccurred()) 70 packageDir = filepath.Join(tmpDir, "pegomocktest") 71 } 72 Expect(os.MkdirAll(packageDir, 0755)).To(Succeed()) 73 subPackageDir = joinPath(packageDir, "subpackage") 74 Expect(os.MkdirAll(subPackageDir, 0755)).To(Succeed()) 75 vendorPackageDir = joinPath(packageDir, "vendor", "github.com", "petergtz", "vendored_package") 76 Expect(os.MkdirAll(vendorPackageDir, 0755)).To(Succeed()) 77 78 var e error 79 origWorkingDir, e = os.Getwd() 80 Expect(e).NotTo(HaveOccurred()) 81 os.Chdir(packageDir) 82 83 if useGoModules { 84 WriteFile(joinPath(packageDir, "go.mod"), 85 `module pegomocktest 86 go 1.12 87 require ( 88 github.com/onsi/gomega v1.5.0 // indirect 89 github.com/petergtz/pegomock v2.3.0+incompatible 90 )`) 91 } 92 93 WriteFile(joinPath(packageDir, "mydisplay.go"), 94 "package pegomocktest; type MyDisplay interface { Show(something string) }") 95 WriteFile(joinPath(packageDir, "http_request_handler.go"), 96 `package pegomocktest; import "net/http"; type RequestHandler interface { Handler(r *http.Request) }`) 97 WriteFile(joinPath(subPackageDir, "subdisplay.go"), 98 "package subpackage; type SubDisplay interface { ShowMe() }") 99 if !useGoModules { 100 WriteFile(joinPath(vendorPackageDir, "iface.go"), 101 `package vendored_package; type Interface interface{ Foobar() }`) 102 WriteFile(joinPath(packageDir, "vendordisplay.go"), `package pegomocktest 103 import ( "github.com/petergtz/vendored_package" ) 104 type VendorDisplay interface { Show(something vendored_package.Interface) }`) 105 } 106 107 app = kingpin.New("pegomock", "Generates mocks based on interfaces.") 108 app.Terminate(func(int) { panic("Unexpected terminate") }) 109 }) 110 111 AfterEach(func() { 112 Expect(os.RemoveAll(packageDir)).To(Succeed()) 113 os.Chdir(origWorkingDir) 114 }) 115 116 Describe(`"generate" command`, func() { 117 118 Context(`with args "MyDisplay"`, func() { 119 120 It(`generates a file mock_mydisplay_test.go that contains "package pegomocktest_test"`, func() { 121 // The rationale behind this is: 122 // mocks should always be part of test packages, because we don't 123 // want them to be part of the production code. 124 // But to be useful, they must still reside in the package, where 125 // they are actually used. 126 127 main.Run(cmd("pegomock generate MyDisplay"), os.Stdout, os.Stdin, app, done) 128 129 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll( 130 BeAnExistingFile(), 131 BeAFileContainingSubString("package pegomocktest_test"))) 132 }) 133 134 It(`sets the default mock name "DisplayMock"`, func() { 135 main.Run(cmd("pegomock generate MyDisplay"), os.Stdout, os.Stdin, app, done) 136 137 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll( 138 BeAnExistingFile(), 139 BeAFileContainingSubString("MockMyDisplay"))) 140 }) 141 }) 142 143 Context(`with args "VendorDisplay""`, func() { 144 145 It(`generates a file mock_vendordisplay_test.go that contains 'import ( vendored_package "github.com/petergtz/vendored_package" )'`, func() { 146 if useGoModules { 147 Skip("Vendoring not supported with Go modules yet") 148 } 149 150 main.Run(cmd("pegomock generate -m VendorDisplay"), os.Stdout, os.Stdin, app, done) 151 Expect(joinPath(packageDir, "mock_vendordisplay_test.go")).To(SatisfyAll( 152 BeAnExistingFile(), 153 BeAFileContainingSubString(`vendored_package "github.com/petergtz/vendored_package"`))) 154 Expect(joinPath(packageDir, "matchers", "vendored_package_interface.go")).To(SatisfyAll( 155 BeAnExistingFile(), 156 BeAFileContainingSubString(`vendored_package "github.com/petergtz/vendored_package"`))) 157 }) 158 }) 159 160 Context(`with args "pegomocktest/subpackage SubDisplay"`, func() { 161 It(`generates a file mock_subdisplay_test.go in "pegomocktest" that contains "package pegomocktest_test"`, func() { 162 main.Run(cmd("pegomock generate pegomocktest/subpackage SubDisplay"), os.Stdout, os.Stdin, app, done) 163 164 Expect(joinPath(packageDir, "mock_subdisplay_test.go")).To(SatisfyAll( 165 BeAnExistingFile(), 166 BeAFileContainingSubString("package pegomocktest_test"))) 167 }) 168 }) 169 170 Context("with args mydisplay.go", func() { 171 It(`generates a file mock_mydisplay_test.go that contains "package pegomocktest_test"`, func() { 172 main.Run(cmd("pegomock generate mydisplay.go"), os.Stdout, os.Stdin, app, done) 173 174 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll( 175 BeAnExistingFile(), 176 BeAFileContainingSubString("package pegomocktest_test"), 177 BeAFileContainingSubString("MockMyDisplay"), 178 )) 179 }) 180 }) 181 182 Context("with args -d mydisplay.go", func() { 183 It(`prints out debug information on stdout`, func() { 184 var buf bytes.Buffer 185 main.Run(cmd("pegomock generate -d mydisplay.go"), &buf, os.Stdin, app, done) 186 Expect(buf.String()).To(ContainSubstring("- method Show")) 187 }) 188 }) 189 190 Context("with args -o where output override is a path with a non-existing directory", func() { 191 It(`creates an output directory before code generation`, func() { 192 var buf bytes.Buffer 193 main.Run(cmd("pegomock generate mydisplay.go -o testoutput/test.go"), &buf, os.Stdin, app, done) 194 Expect(joinPath(packageDir, "testoutput/test.go")).To(SatisfyAll( 195 BeAnExistingFile(), 196 BeAFileContainingSubString("package pegomocktest_test"))) 197 }) 198 }) 199 200 Context("with args --output-dir", func() { 201 It(`creates the mocks in output dir with the dir's basename as package name`, func() { 202 var buf bytes.Buffer 203 main.Run(cmd("pegomock generate MyDisplay --output-dir fakes"), &buf, os.Stdin, app, done) 204 Expect(joinPath(packageDir, "fakes/mock_mydisplay.go")).To(SatisfyAll( 205 BeAnExistingFile(), 206 BeAFileContainingSubString("package fakes"))) 207 }) 208 }) 209 210 Context("with args --output-dir and --package", func() { 211 It(`creates the mocks in output dir with the specified package name`, func() { 212 var buf bytes.Buffer 213 main.Run(cmd("pegomock generate MyDisplay --output-dir fakes --package other"), &buf, os.Stdin, app, done) 214 215 Expect(joinPath(packageDir, "fakes/mock_mydisplay.go")).To(SatisfyAll( 216 BeAnExistingFile(), 217 BeAFileContainingSubString("package other"))) 218 }) 219 }) 220 221 Context("with args --mock-name", func() { 222 It(`sets the mock name as given`, func() { 223 main.Run(cmd("pegomock generate MyDisplay --mock-name RenamedMock"), os.Stdout, os.Stdin, app, done) 224 225 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll( 226 BeAnExistingFile(), 227 BeAFileContainingSubString("RenamedMock"))) 228 }) 229 }) 230 231 Context("with args for specifying matcher directory", func() { 232 It(`creates matchers in the specified directory`, func() { 233 if useGoModules { 234 Skip("Vendoring not supported with Go modules yet") 235 } 236 var buf bytes.Buffer 237 main.Run(cmd("pegomock generate --generate-matchers --matchers-dir custom/matcher/dir VendorDisplay"), &buf, os.Stdin, app, done) 238 239 Expect(joinPath(packageDir, "custom/matcher/dir")).To(BeADirectory()) 240 Expect(joinPath(packageDir, "custom/matcher/dir/vendored_package_interface.go")).To(BeAnExistingFile()) 241 }) 242 }) 243 244 Context("with too many args", func() { 245 246 It(`reports an error and the usage`, func() { 247 var buf bytes.Buffer 248 Expect(func() { 249 main.Run(cmd("pegomock generate with too many args"), &buf, os.Stdin, app, done) 250 }).To(Panic()) 251 252 Expect(buf.String()).To(ContainSubstring("Please provide exactly 1 interface or 1 package + 1 interface")) 253 Expect(buf.String()).To(ContainSubstring("usage")) 254 }) 255 }) 256 257 }) 258 259 Describe(`"watch" command`, func() { 260 261 AfterEach(func(testDone Done) { done <- true; close(testDone) }, 3) 262 263 Context("with no further action", func() { 264 It(`Creates a template file interfaces_to_mock in the current directory`, func() { 265 go main.Run(cmd("pegomock watch"), os.Stdout, os.Stdin, app, done) 266 Eventually(func() string { return "interfaces_to_mock" }, "3s").Should(BeAnExistingFile()) 267 }) 268 }) 269 270 Context("after populating interfaces_to_mock with an actual interface", func() { 271 It(`Eventually creates a file mock_mydisplay_test.go starting with "package pegomocktest_test"`, func() { 272 WriteFile(joinPath(packageDir, "interfaces_to_mock"), "MyDisplay") 273 274 go main.Run(cmd("pegomock watch"), os.Stdout, os.Stdin, app, done) 275 276 Eventually(joinPath(packageDir, "mock_mydisplay_test.go"), "3s").Should(SatisfyAll( 277 BeAnExistingFile(), 278 BeAFileContainingSubString("package pegomocktest_test"))) 279 }) 280 }) 281 282 }) 283 284 Describe(`"remove" command`, func() { 285 Context("there are no mock files", func() { 286 It("removes mock files in current directory only", func() { 287 var buf bytes.Buffer 288 289 main.Run(cmd("pegomock remove -n"), &buf, os.Stdin, app, done) 290 291 Expect(buf.String()).To(ContainSubstring(`No files to remove.`)) 292 }) 293 }) 294 295 Context("there are some mock files", func() { 296 BeforeEach(func() { 297 main.Run(cmd("pegomock generate MyDisplay"), os.Stdout, os.Stdin, app, done) 298 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(SatisfyAll( 299 BeAnExistingFile(), 300 BeAFileContainingSubString("package pegomocktest_test"))) 301 302 main.Run(cmd("pegomock generate --output-dir "+subPackageDir+" pegomocktest/subpackage SubDisplay"), os.Stdout, os.Stdin, app, done) 303 304 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(SatisfyAll( 305 BeAnExistingFile(), 306 BeAFileContainingSubString("package subpackage"))) 307 }) 308 309 Context("Non-interactive", func() { 310 Context("non-recursive", func() { 311 It("removes mock files in current directory only", func() { 312 var buf bytes.Buffer 313 314 main.Run(cmd("pegomock remove -n"), &buf, os.Stdin, app, done) 315 316 Expect(buf.String()).To(ContainSubstring(`Deleting the following files: 317 ` + packageDir + `/mock_mydisplay_test.go`)) 318 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile()) 319 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile()) 320 }) 321 }) 322 323 Context("recursive", func() { 324 It("removes mock files recursively", func() { 325 var buf bytes.Buffer 326 327 main.Run(cmd("pegomock remove -n -r"), &buf, os.Stdin, app, done) 328 329 Expect(buf.String()).To(ContainSubstring(`Deleting the following files: 330 ` + packageDir + `/mock_mydisplay_test.go 331 ` + packageDir + `/subpackage/mock_subdisplay.go`)) 332 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile()) 333 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).NotTo(BeAnExistingFile()) 334 }) 335 336 Context("custom matchers were generated", func() { 337 BeforeEach(func() { 338 main.Run(cmd("pegomock generate -m RequestHandler"), os.Stdout, os.Stdin, app, done) 339 Expect(joinPath(packageDir, "mock_requesthandler_test.go")).To(SatisfyAll( 340 BeAnExistingFile(), 341 BeAFileContainingSubString("package pegomocktest_test"))) 342 343 Expect(joinPath(packageDir, "matchers", "ptr_to_http_request.go")).To(SatisfyAll( 344 BeAnExistingFile(), 345 BeAFileContainingSubString("package matchers"), 346 )) 347 main.Run(cmd("pegomock generate -m --output-dir "+subPackageDir+" RequestHandler"), os.Stdout, os.Stdin, app, done) 348 349 Expect(joinPath(subPackageDir, "mock_requesthandler.go")).To(SatisfyAll( 350 BeAnExistingFile(), 351 BeAFileContainingSubString("package subpackage"))) 352 Expect(joinPath(subPackageDir, "matchers", "ptr_to_http_request.go")).To(SatisfyAll( 353 BeAnExistingFile(), 354 BeAFileContainingSubString("package matchers"), 355 )) 356 357 }) 358 359 It("removes matchers and matchers dir too", func() { 360 var buf bytes.Buffer 361 362 main.Run(cmd("pegomock remove -n -r"), &buf, os.Stdin, app, done) 363 364 Expect(buf.String()).To(Equal(`Deleting the following files: 365 `+packageDir+`/matchers 366 `+packageDir+`/matchers/ptr_to_http_request.go 367 `+packageDir+`/mock_mydisplay_test.go 368 `+packageDir+`/mock_requesthandler_test.go 369 `+packageDir+`/subpackage/matchers 370 `+packageDir+`/subpackage/matchers/ptr_to_http_request.go 371 `+packageDir+`/subpackage/mock_requesthandler.go 372 `+packageDir+`/subpackage/mock_subdisplay.go 373 `), buf.String()) 374 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile()) 375 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).NotTo(BeAnExistingFile()) 376 }) 377 }) 378 }) 379 380 Context("Silent", func() { 381 It("removes mock files, but provides no output", func() { 382 var buf bytes.Buffer 383 384 main.Run(cmd("pegomock remove -n --silent"), &buf, os.Stdin, app, done) 385 386 Expect(buf.String()).To(BeEmpty()) 387 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile()) 388 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile()) 389 }) 390 }) 391 392 Context("with path", func() { 393 It("removes mock files in path", func() { 394 var buf bytes.Buffer 395 396 main.Run(cmd("pegomock remove -n "+subPackageDir), &buf, os.Stdin, app, done) 397 398 Expect(buf.String()).To(ContainSubstring(`Deleting the following files: 399 ` + packageDir + `/subpackage/mock_subdisplay.go`)) 400 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(BeAnExistingFile()) 401 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).NotTo(BeAnExistingFile()) 402 }) 403 }) 404 }) 405 406 Context("Interactive", func() { 407 Context("confirming with yes", func() { 408 It("removes mock files", func() { 409 var buf bytes.Buffer 410 411 main.Run(cmd("pegomock remove"), &buf, strings.NewReader("yes\n"), app, done) 412 413 Expect(buf.String()).To(ContainSubstring(`Will delete the following files: 414 ` + packageDir + `/mock_mydisplay_test.go 415 Continue? [y/n]:`)) 416 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).NotTo(BeAnExistingFile()) 417 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile()) 418 }) 419 }) 420 421 Context("confirming with no", func() { 422 It("does not remove mock files", func() { 423 var buf bytes.Buffer 424 425 main.Run(cmd("pegomock remove"), &buf, strings.NewReader("no\n"), app, done) 426 427 Expect(buf.String()).To(ContainSubstring(`Will delete the following files: 428 ` + packageDir + `/mock_mydisplay_test.go 429 Continue? [y/n]:`)) 430 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(BeAnExistingFile()) 431 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile()) 432 }) 433 }) 434 }) 435 436 Context("dry-run", func() { 437 It("removes no mock files, but provides files that would be deleted", func() { 438 var buf bytes.Buffer 439 440 main.Run(cmd("pegomock remove --dry-run"), &buf, os.Stdin, app, done) 441 442 Expect(buf.String()).To(ContainSubstring(`Would delete the following files: 443 ` + packageDir + `/mock_mydisplay_test.go`)) 444 Expect(joinPath(packageDir, "mock_mydisplay_test.go")).To(BeAnExistingFile()) 445 Expect(joinPath(subPackageDir, "mock_subdisplay.go")).To(BeAnExistingFile()) 446 }) 447 }) 448 }) 449 }) 450 451 Context("with some unknown command", func() { 452 It(`reports an error and the usage`, func() { 453 var buf bytes.Buffer 454 kingpin.CommandLine.Terminate(nil) 455 kingpin.CommandLine.Writer(&buf) 456 457 main.Run(cmd("pegomock some unknown command"), &buf, os.Stdin, app, done) 458 Expect(buf.String()).To(ContainSubstring("error")) 459 }) 460 }) 461 462 }) 463 return nil 464 } 465 func cmd(line string) []string { 466 return strings.Split(line, " ") 467 }