If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThe growVelociraptor()
method is called two times. And thanks to the two arguments to willReturn()
, $dino1
will be returned first, then $dino2
. But we can control this much more than we are now.
Check it out: copy the beginning of the promise and paste it below. Replace the argument with 5. Then say ->willReturn($dino2)
. For the first promise, remove $dino2
: this will now return $dino1
every time its matched.
... lines 1 - 18 | |
function it_builds_enclosure_with_dinosaurs(DinosaurFactory $dinosaurFactory) | |
{ | |
... lines 21 - 25 | |
$dinosaurFactory->growVelociraptor(Argument::type('integer'))->willReturn( | |
$dino1 | |
); | |
$dinosaurFactory->growVelociraptor(5)->willReturn( | |
$dino2 | |
); | |
... lines 32 - 38 | |
} | |
... lines 40 - 41 |
So... what do you think will happen now? Down here, I'm saying that when growVelociraptor()
is called with exactly the number 5
, return $dino2
. But up here I'm saying: if growVelociraptor()
is called with any integer
, return $dino1
. So... when growVelociraptor()
is called with the number 5, what's going to be returned? $dino1
? $dino2
? A mystery $dino3
!? Does the order of these promises matter? Or something else? Is there a 9th planet in the solar system that we haven't discovered yet?
I don't know! Let's find out! Or at least, answer a few of these questions. Try phpspec:
./vendor/bin/phpspec run spec/Service/EnclosureBuilderServiceSpec.php:19
It fails: expected Dinosaur
but got Dinosaur
. Wow... umm... that's not very helpful. Re-run with -v
:
./vendor/bin/phpspec run spec/Service/EnclosureBuilderServiceSpec.php:19 -v
Ah! Now we can see that, on line 38, so when we're checking the first dinosaur, we expect it to be our big Stegosaurus, but it was actually our Baby Stegosaurus
.
This is really cool! Each time we call growVelociraptor()
, it looks at all of these method promises and finds the most specific one. When the argument is 5, it matches both promises: it is 5, but it's also an integer. But because the second is more specific, that promise is used and it returns $dino2
.
So, the first time we go through the loop, 5 is the argument and we return $dino2
. The second time, the argument is 6 and only matches the first promise. So, $dino1
is returned.
This means that $dino2
is now the first item and $dino1
is the second. How cool is that?! That's full, beautiful control.
... lines 1 - 18 | |
function it_builds_enclosure_with_dinosaurs(DinosaurFactory $dinosaurFactory) | |
{ | |
... lines 21 - 36 | |
$enclosure->getDinosaurs()[0]->shouldBe($dino2); | |
$enclosure->getDinosaurs()[1]->shouldBe($dino1); | |
} | |
... lines 40 - 41 |
Let's take off the -v
option and run the tests:
./vendor/bin/phpspec run spec/Service/EnclosureBuilderServiceSpec.php:19
Got it! To prove how this works, we can even re-order the two promises. Yep, makes no difference.
./vendor/bin/phpspec run spec/Service/EnclosureBuilderServiceSpec.php:19
So, whenever a method is called on a stub, prophecy will look at all the promises for that method and find the best one. And if none are found... but at least one exists, error!
Let's change this back to just one promise: we don't really need to be this specific. Delete the more specific call and make the other one return $dino1
and then $dino2
just like before. Also update the asserts to go back to the original way. Double-check that phpspec is happy:
... lines 1 - 18 | |
function it_builds_enclosure_with_dinosaurs(DinosaurFactory $dinosaurFactory) | |
{ | |
... lines 21 - 25 | |
$dinosaurFactory->growVelociraptor(Argument::type('integer'))->willReturn( | |
$dino1, | |
$dino2 | |
); | |
... lines 30 - 34 | |
$enclosure->getDinosaurs()[0]->shouldBe($dino1); | |
$enclosure->getDinosaurs()[1]->shouldBe($dino2); | |
} | |
... lines 38 - 39 |
./vendor/bin/phpspec run spec/Service/EnclosureBuilderServiceSpec.php:19
Woohoo! Arguments are a great way to very specifically control what values are returned based on the input. And to make sure your methods are being called with the arguments you expect.
Next: instead of controlling the return value of methods, let's talk about adding expectations to our test doubles - like making sure that a method was called an exact number of times. Yep, it's mocks and spies time!
"Houston: no signs of life"
Start the conversation!