gluon-web-remote

Web remote Administration for big size of gluon routers
git clone git://archive.git.mtrnord.blog/MTRNord/gluon-web-remote.git
Log | Files | Refs | README

README.md (14471B)


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