PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/phpspec/prophecy/README.md

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