1# Prophecy 2 3[![Stable release](https://poser.pugx.org/phpspec/prophecy/version.svg)](https://packagist.org/packages/phpspec/prophecy) 4[![Build Status](https://travis-ci.org/phpspec/prophecy.svg?branch=master)](https://travis-ci.org/phpspec/prophecy) 5 6Prophecy is a highly opinionated yet very powerful and flexible PHP object mocking 7framework. Though initially it was created to fulfil phpspec2 needs, it is flexible 8enough to be used inside any testing framework out there with minimal effort. 9 10## A simple example 11 12```php 13<?php 14 15class UserTest extends PHPUnit_Framework_TestCase 16{ 17 private $prophet; 18 19 public function testPasswordHashing() 20 { 21 $hasher = $this->prophet->prophesize('App\Security\Hasher'); 22 $user = new App\Entity\User($hasher->reveal()); 23 24 $hasher->generateHash($user, 'qwerty')->willReturn('hashed_pass'); 25 26 $user->setPassword('qwerty'); 27 28 $this->assertEquals('hashed_pass', $user->getPassword()); 29 } 30 31 protected function setup() 32 { 33 $this->prophet = new \Prophecy\Prophet; 34 } 35 36 protected function tearDown() 37 { 38 $this->prophet->checkPredictions(); 39 } 40} 41``` 42 43## Installation 44 45### Prerequisites 46 47Prophecy requires PHP 5.3.3 or greater. 48 49### Setup through composer 50 51First, add Prophecy to the list of dependencies inside your `composer.json`: 52 53```json 54{ 55 "require-dev": { 56 "phpspec/prophecy": "~1.0" 57 } 58} 59``` 60 61Then simply install it with composer: 62 63```bash 64$> composer install --prefer-dist 65``` 66 67You can read more about Composer on its [official webpage](http://getcomposer.org). 68 69## How to use it 70 71First of all, in Prophecy every word has a logical meaning, even the name of the library 72itself (Prophecy). When you start feeling that, you'll become very fluid with this 73tool. 74 75For example, Prophecy has been named that way because it concentrates on describing the future 76behavior of objects with very limited knowledge about them. But as with any other prophecy, 77those object prophecies can't create themselves - there should be a Prophet: 78 79```php 80$prophet = new Prophecy\Prophet; 81``` 82 83The Prophet creates prophecies by *prophesizing* them: 84 85```php 86$prophecy = $prophet->prophesize(); 87``` 88 89The result of the `prophesize()` method call is a new object of class `ObjectProphecy`. Yes, 90that's your specific object prophecy, which describes how your object would behave 91in the near future. But first, you need to specify which object you're talking about, 92right? 93 94```php 95$prophecy->willExtend('stdClass'); 96$prophecy->willImplement('SessionHandlerInterface'); 97``` 98 99There are 2 interesting calls - `willExtend` and `willImplement`. The first one tells 100object prophecy that our object should extend specific class, the second one says that 101it should implement some interface. Obviously, objects in PHP can implement multiple 102interfaces, but extend only one parent class. 103 104### Dummies 105 106Ok, now we have our object prophecy. What can we do with it? First of all, we can get 107our object *dummy* by revealing its prophecy: 108 109```php 110$dummy = $prophecy->reveal(); 111``` 112 113The `$dummy` variable now holds a special dummy object. Dummy objects are objects that extend 114and/or implement preset classes/interfaces by overriding all their public methods. The key 115point about dummies is that they do not hold any logic - they just do nothing. Any method 116of the dummy will always return `null` and the dummy will never throw any exceptions. 117Dummy is your friend if you don't care about the actual behavior of this double and just need 118a token object to satisfy a method typehint. 119 120You need to understand one thing - a dummy is not a prophecy. Your object prophecy is still 121assigned to `$prophecy` variable and in order to manipulate with your expectations, you 122should work with it. `$dummy` is a dummy - a simple php object that tries to fulfil your 123prophecy. 124 125### Stubs 126 127Ok, now we know how to create basic prophecies and reveal dummies from them. That's 128awesome if we don't care about our _doubles_ (objects that reflect originals) 129interactions. If we do, we need to use *stubs* or *mocks*. 130 131A stub is an object double, which doesn't have any expectations about the object behavior, 132but when put in specific environment, behaves in specific way. Ok, I know, it's cryptic, 133but bear with me for a minute. Simply put, a stub is a dummy, which depending on the called 134method signature does different things (has logic). To create stubs in Prophecy: 135 136```php 137$prophecy->read('123')->willReturn('value'); 138``` 139 140Oh wow. We've just made an arbitrary call on the object prophecy? Yes, we did. And this 141call returned us a new object instance of class `MethodProphecy`. Yep, that's a specific 142method with arguments prophecy. Method prophecies give you the ability to create method 143promises or predictions. We'll talk about method predictions later in the _Mocks_ section. 144 145#### Promises 146 147Promises are logical blocks, that represent your fictional methods in prophecy terms 148and they are handled by the `MethodProphecy::will(PromiseInterface $promise)` method. 149As a matter of fact, the call that we made earlier (`willReturn('value')`) is a simple 150shortcut to: 151 152```php 153$prophecy->read('123')->will(new Prophecy\Promise\ReturnPromise(array('value'))); 154``` 155 156This promise will cause any call to our double's `read()` method with exactly one 157argument - `'123'` to always return `'value'`. But that's only for this 158promise, there's plenty others you can use: 159 160- `ReturnPromise` or `->willReturn(1)` - returns a value from a method call 161- `ReturnArgumentPromise` or `->willReturnArgument($index)` - returns the nth method argument from call 162- `ThrowPromise` or `->willThrow($exception)` - causes the method to throw specific exception 163- `CallbackPromise` or `->will($callback)` - gives you a quick way to define your own custom logic 164 165Keep in mind, that you can always add even more promises by implementing 166`Prophecy\Promise\PromiseInterface`. 167 168#### Method prophecies idempotency 169 170Prophecy enforces same method prophecies and, as a consequence, same promises and 171predictions for the same method calls with the same arguments. This means: 172 173```php 174$methodProphecy1 = $prophecy->read('123'); 175$methodProphecy2 = $prophecy->read('123'); 176$methodProphecy3 = $prophecy->read('321'); 177 178$methodProphecy1 === $methodProphecy2; 179$methodProphecy1 !== $methodProphecy3; 180``` 181 182That's interesting, right? Now you might ask me how would you define more complex 183behaviors where some method call changes behavior of others. In PHPUnit or Mockery 184you do that by predicting how many times your method will be called. In Prophecy, 185you'll use promises for that: 186 187```php 188$user->getName()->willReturn(null); 189 190// For PHP 5.4 191$user->setName('everzet')->will(function () { 192 $this->getName()->willReturn('everzet'); 193}); 194 195// For PHP 5.3 196$user->setName('everzet')->will(function ($args, $user) { 197 $user->getName()->willReturn('everzet'); 198}); 199 200// Or 201$user->setName('everzet')->will(function ($args) use ($user) { 202 $user->getName()->willReturn('everzet'); 203}); 204``` 205 206And now it doesn't matter how many times or in which order your methods are called. 207What matters is their behaviors and how well you faked it. 208 209#### Arguments wildcarding 210 211The previous example is awesome (at least I hope it is for you), but that's not 212optimal enough. We hardcoded `'everzet'` in our expectation. Isn't there a better 213way? In fact there is, but it involves understanding what this `'everzet'` 214actually is. 215 216You see, even if method arguments used during method prophecy creation look 217like simple method arguments, in reality they are not. They are argument token 218wildcards. As a matter of fact, `->setName('everzet')` looks like a simple call just 219because Prophecy automatically transforms it under the hood into: 220 221```php 222$user->setName(new Prophecy\Argument\Token\ExactValueToken('everzet')); 223``` 224 225Those argument tokens are simple PHP classes, that implement 226`Prophecy\Argument\Token\TokenInterface` and tell Prophecy how to compare real arguments 227with your expectations. And yes, those classnames are damn big. That's why there's a 228shortcut class `Prophecy\Argument`, which you can use to create tokens like that: 229 230```php 231use Prophecy\Argument; 232 233$user->setName(Argument::exact('everzet')); 234``` 235 236`ExactValueToken` is not very useful in our case as it forced us to hardcode the username. 237That's why Prophecy comes bundled with a bunch of other tokens: 238 239- `IdenticalValueToken` or `Argument::is($value)` - checks that the argument is identical to a specific value 240- `ExactValueToken` or `Argument::exact($value)` - checks that the argument matches a specific value 241- `TypeToken` or `Argument::type($typeOrClass)` - checks that the argument matches a specific type or 242 classname 243- `ObjectStateToken` or `Argument::which($method, $value)` - checks that the argument method returns 244 a specific value 245- `CallbackToken` or `Argument::that(callback)` - checks that the argument matches a custom callback 246- `AnyValueToken` or `Argument::any()` - matches any argument 247- `AnyValuesToken` or `Argument::cetera()` - matches any arguments to the rest of the signature 248- `StringContainsToken` or `Argument::containingString($value)` - checks that the argument contains a specific string value 249 250And you can add even more by implementing `TokenInterface` with your own custom classes. 251 252So, let's refactor our initial `{set,get}Name()` logic with argument tokens: 253 254```php 255use Prophecy\Argument; 256 257$user->getName()->willReturn(null); 258 259// For PHP 5.4 260$user->setName(Argument::type('string'))->will(function ($args) { 261 $this->getName()->willReturn($args[0]); 262}); 263 264// For PHP 5.3 265$user->setName(Argument::type('string'))->will(function ($args, $user) { 266 $user->getName()->willReturn($args[0]); 267}); 268 269// Or 270$user->setName(Argument::type('string'))->will(function ($args) use ($user) { 271 $user->getName()->willReturn($args[0]); 272}); 273``` 274 275That's it. Now our `{set,get}Name()` prophecy will work with any string argument provided to it. 276We've just described how our stub object should behave, even though the original object could have 277no behavior whatsoever. 278 279One last bit about arguments now. You might ask, what happens in case of: 280 281```php 282use Prophecy\Argument; 283 284$user->getName()->willReturn(null); 285 286// For PHP 5.4 287$user->setName(Argument::type('string'))->will(function ($args) { 288 $this->getName()->willReturn($args[0]); 289}); 290 291// For PHP 5.3 292$user->setName(Argument::type('string'))->will(function ($args, $user) { 293 $user->getName()->willReturn($args[0]); 294}); 295 296// Or 297$user->setName(Argument::type('string'))->will(function ($args) use ($user) { 298 $user->getName()->willReturn($args[0]); 299}); 300 301$user->setName(Argument::any())->will(function () { 302}); 303``` 304 305Nothing. Your stub will continue behaving the way it did before. That's because of how 306arguments wildcarding works. Every argument token type has a different score level, which 307wildcard then uses to calculate the final arguments match score and use the method prophecy 308promise that has the highest score. In this case, `Argument::type()` in case of success 309scores `5` and `Argument::any()` scores `3`. So the type token wins, as does the first 310`setName()` method prophecy and its promise. The simple rule of thumb - more precise token 311always wins. 312 313#### Getting stub objects 314 315Ok, now we know how to define our prophecy method promises, let's get our stub from 316it: 317 318```php 319$stub = $prophecy->reveal(); 320``` 321 322As you might see, the only difference between how we get dummies and stubs is that with 323stubs we describe every object conversation instead of just agreeing with `null` returns 324(object being *dummy*). As a matter of fact, after you define your first promise 325(method call), Prophecy will force you to define all the communications - it throws 326the `UnexpectedCallException` for any call you didn't describe with object prophecy before 327calling it on a stub. 328 329### Mocks 330 331Now we know how to define doubles without behavior (dummies) and doubles with behavior, but 332no expectations (stubs). What's left is doubles for which we have some expectations. These 333are called mocks and in Prophecy they look almost exactly the same as stubs, except that 334they define *predictions* instead of *promises* on method prophecies: 335 336```php 337$entityManager->flush()->shouldBeCalled(); 338``` 339 340#### Predictions 341 342The `shouldBeCalled()` method here assigns `CallPrediction` to our method prophecy. 343Predictions are a delayed behavior check for your prophecies. You see, during the entire lifetime 344of your doubles, Prophecy records every single call you're making against it inside your 345code. After that, Prophecy can use this collected information to check if it matches defined 346predictions. You can assign predictions to method prophecies using the 347`MethodProphecy::should(PredictionInterface $prediction)` method. As a matter of fact, 348the `shouldBeCalled()` method we used earlier is just a shortcut to: 349 350```php 351$entityManager->flush()->should(new Prophecy\Prediction\CallPrediction()); 352``` 353 354It checks if your method of interest (that matches both the method name and the arguments wildcard) 355was called 1 or more times. If the prediction failed then it throws an exception. When does this 356check happen? Whenever you call `checkPredictions()` on the main Prophet object: 357 358```php 359$prophet->checkPredictions(); 360``` 361 362In PHPUnit, you would want to put this call into the `tearDown()` method. If no predictions 363are defined, it would do nothing. So it won't harm to call it after every test. 364 365There are plenty more predictions you can play with: 366 367- `CallPrediction` or `shouldBeCalled()` - checks that the method has been called 1 or more times 368- `NoCallsPrediction` or `shouldNotBeCalled()` - checks that the method has not been called 369- `CallTimesPrediction` or `shouldBeCalledTimes($count)` - checks that the method has been called 370 `$count` times 371- `CallbackPrediction` or `should($callback)` - checks the method against your own custom callback 372 373Of course, you can always create your own custom prediction any time by implementing 374`PredictionInterface`. 375 376### Spies 377 378The last bit of awesomeness in Prophecy is out-of-the-box spies support. As I said in the previous 379section, Prophecy records every call made during the double's entire lifetime. This means 380you don't need to record predictions in order to check them. You can also do it 381manually by using the `MethodProphecy::shouldHave(PredictionInterface $prediction)` method: 382 383```php 384$em = $prophet->prophesize('Doctrine\ORM\EntityManager'); 385 386$controller->createUser($em->reveal()); 387 388$em->flush()->shouldHaveBeenCalled(); 389``` 390 391Such manipulation with doubles is called spying. And with Prophecy it just works. 392