└── readme.md /readme.md: -------------------------------------------------------------------------------- 1 | PHPSpec reference guide 2 | ======================= 3 | 4 | A guide containing phpspec snippets for different use cases 5 | 6 | * [Commands](#commands) 7 | * [Mocks](#mocks) 8 | * [Exceptions](#exceptions) 9 | * [Matching types](#matching-types) 10 | * [Dynamic matching types](#dynamic-matching-types) 11 | * [Plugins](#plugins) 12 | * [Templates](#templates) 13 | * [Tips](#tips) 14 | 15 | ## Installation 16 | 17 | Install with composer using this composer command: 18 | 19 | ``` 20 | composer require phpspec/phpspec:2.*@dev --dev 21 | ``` 22 | 23 | Or by adding the depencency manually to your composer.json file: 24 | 25 | ```json 26 | { 27 | "require-dev": { 28 | "phpspec/phpspec": "2.0.*@dev" 29 | } 30 | } 31 | ``` 32 | 33 | Optionally, add the following aliases to your system: 34 | 35 | ```bash 36 | # PHPSpec Command 37 | 38 | alias phpspec='vendor/bin/phpspec' 39 | 40 | # PHPSpec Shortcuts 41 | 42 | alias spec='vendor/bin/phpspec' 43 | alias specr='vendor/bin/phpspec run' 44 | alias specd='vendor/bin/phpspec describe' 45 | ``` 46 | 47 | 48 | ## Commands 49 | 50 | ### Describe 51 | 52 | `phpspec describe Namespaced/Class/Name` 53 | 54 | Describe creates the spec file for the class you want to specify. 55 | 56 | ### Run 57 | 58 | `phpspec run` 59 | 60 | Run executes the specs and ends with a nice output. 61 | 62 | ![PhpSpec output](https://i.cloudup.com/EIw8gDuYrr-2000x2000.png) 63 | 64 | ## Mocks 65 | 66 | ### Creating mocks 67 | 68 | #### Injecting mocks in constructor 69 | 70 | Inject a mock in the constructor for use in your specs: 71 | 72 | ```php 73 | // spec/Acme/HandlerSpec.php 74 | 75 | function let(Acme\Dependency $dependency) 76 | { 77 | $this->beConstructedWith($dependency); 78 | } 79 | 80 | // Alternative syntax 81 | function let($dependency) 82 | { 83 | $dependency->beADoubleOf('Acme\Dependency'); 84 | $this->beConstructedWith($dependency); 85 | } 86 | ``` 87 | 88 | Pass in the spec the same mock variable name: 89 | 90 | ```php 91 | // Our spec 92 | function it_sends_the_message($dependency) 93 | { 94 | // $dependency is now the mock we created in let() 95 | } 96 | ``` 97 | 98 | #### Injecting mocks in spec 99 | 100 | ```php 101 | function it_sends_the_message(Acme\Dependency $dependency) 102 | { 103 | // $dependency is now a new mock of Acme\Dependency 104 | } 105 | ``` 106 | 107 | ### Using mocks 108 | 109 | #### Returning values from mock methods 110 | 111 | Imagine you have a Messenger class, containing a `send(Message $message)` method. While we run `send($message)` we want to make sure $message will return 'abc' when executed. 112 | 113 | ```php 114 | function it_sends_the_message(Acme\Message $message) 115 | { 116 | $message->getContents()->willReturn('abc'); 117 | $this->send($message); 118 | } 119 | ``` 120 | 121 | Now imagine you need to call a method several times and have a diferent value each time. 122 | 123 | ```php 124 | function it_gets_three_random_numbers(Acme\RandomGenerator $rand) 125 | { 126 | $rand->generate()->willReturn(123, 432, 874); 127 | $this->getNumbers($rand, 3)->shouldReturn([123, 432, 874]); 128 | } 129 | ``` 130 | 131 | #### Specify that a mock method should be called 132 | 133 | Usually we want to make sure that a mock method is executed. We do that with `shouldBeCalled()`. 134 | 135 | ```php 136 | function it_sends_the_message(Acme\Message $message) 137 | { 138 | $message->getContents()->shouldBeCalled()->willReturn('abc'); 139 | $this->send($message); 140 | } 141 | ``` 142 | 143 | ## Exceptions 144 | 145 | ### Throw exception 146 | 147 | Expect an `InvalidArgumentException` to be thrown when we call "send('bad')". Passing the exception type as string prevents the message validation. 148 | 149 | ```php 150 | function it_throws_exception_during_send(Acme\Message $message) 151 | { 152 | $this->shouldThrow('\InvalidArgumentException')->duringSend('bad'); 153 | } 154 | ``` 155 | 156 | ### Throw exception with message 157 | 158 | Expect an `InvalidArgumentException` with message "Wrong argument" to be thrown when we call `send('bad')`: 159 | 160 | ```php 161 | function it_throws_exception_during_send(Acme\Message $message) 162 | { 163 | $exception = new InvalidArgumentException('Wrong argument'); 164 | 165 | $this->shouldThrow($exception)->duringSend('bad'); 166 | } 167 | ``` 168 | 169 | ### Throw exception in constructor 170 | 171 | Expect an `InvalidArgumentException` to be thrown when we call the constructor `__construct('bad1, 'bad2')`. The exception is passed as instance without message. Therefore we expect no message when the exeption is thrown. 172 | 173 | ```php 174 | function it_throws_exception_during_constructor(Acme\Message $message) 175 | { 176 | $this->shouldThrow(new \InvalidArgumentException)->during('__construct', array('bad1', 'bad2')); 177 | } 178 | ``` 179 | 180 | ## Matching types 181 | 182 | ### Identical 183 | 184 | ```php 185 | $this->method()->shouldBe($result); 186 | 187 | // Alternative syntax 188 | $this->method()->shouldReturn($result); 189 | $this->method()->shouldEqual($result); 190 | $this->method()->shouldBeEqualTo($result); 191 | ``` 192 | 193 | ### Equal 194 | 195 | ```php 196 | $this->method()->shouldBeLike($result) 197 | ``` 198 | 199 | ### Data types 200 | 201 | Expect the result of a method has a specific data type. 202 | 203 | ```php 204 | $this->method()->shouldBeBool(); 205 | $this->method()->shouldBeObject(); 206 | $this->method()->shouldBeString(); 207 | $this->method()->shouldBeInteger(); 208 | $this->method()->shouldBeDecimal(); 209 | $this->method()->shouldBeArray(); 210 | ``` 211 | 212 | ### Objects 213 | 214 | All the following syntaxes expect that method() returns an instance of the given class. 215 | 216 | ```php 217 | $this->method()->shouldHaveType('\Full\Class\Name'); 218 | $this->method()->shouldReturnAnInstanceOf('\Full\Class\Name'); 219 | $this->method()->shouldBeAnInstanceOf('\Full\Class\Name'); 220 | $this->method()->shouldImplement('\Full\Class\Name'); 221 | ``` 222 | 223 | ### Count 224 | 225 | You can use `shouldHaveCount()` on a method returning an `array` or an instance of `\Countable`. 226 | 227 | ```php 228 | $this->getArray()->shouldHaveCount(2) 229 | $this->getCollection()->shouldHaveCount(2) 230 | ``` 231 | 232 | ```php 233 | public function getArray() { 234 | return array(1, 2) 235 | } 236 | 237 | public function getCollection() { 238 | return $this->users; 239 | } 240 | ``` 241 | 242 | You can also use `shouldHaveCount()` on `$this`, if it is an instance of `\Countable`. 243 | 244 | ```php 245 | 246 | $this->shouldHaveCount(10); 247 | 248 | class UsersCollection implements \Countable { 249 | 250 | public function count() { 251 | return count($this->users); 252 | } 253 | } 254 | ``` 255 | 256 | ## Dynamic matching types 257 | 258 | ### Boolean shouldBe* 259 | 260 | To use the shouldBe* matching, the class should have a public method starting with "is". 261 | 262 | ```php 263 | // User.php 264 | public function isActive() { 265 | return (bool) $this->isActive; 266 | } 267 | ``` 268 | 269 | ```php 270 | // expect isActive returns true 271 | $this->shouldBeActive(); 272 | 273 | // expect isActive returns false 274 | $this->shouldNotBeActive(); 275 | ``` 276 | 277 | ### Boolean shouldHave* 278 | 279 | To use the shouldHave* matching, the class should have a public method starting with "has". 280 | 281 | ```php 282 | // User.php 283 | public function hasProfile() { 284 | return (bool) $this->hasProfile; 285 | } 286 | ``` 287 | 288 | ```php 289 | // expect hasProfile returns true 290 | $this->shouldHaveProfile(); 291 | 292 | // expect hasProfile returns false 293 | $this->shouldNotHaveProfile(); 294 | ``` 295 | 296 | ### Custom types 297 | 298 | Custom types give us the possibility to expect something using a closure. To do that we have to define a "getMatchers()" method in our spec class. 299 | 300 | #### getMatchers() 301 | 302 | getMatchers() should return an array with keys describing the expectations and values the closures containing the logic of the expectations. The first parameter in the closure is the output of the executed method. 303 | 304 | ```php 305 | 306 | function getMatchers() 307 | { 308 | return array( 309 | 'haveLength' => function($result, $count) { 310 | return strlen($result) == $count; 311 | } 312 | ); 313 | } 314 | ``` 315 | 316 | #### Using getMatchers() 317 | 318 | To use the above expectation we use the method `should` + `{getMatchers key}`. 319 | 320 | ```php 321 | $this->method()->shouldHaveLength(12); 322 | ``` 323 | 324 | ## Plugins 325 | 326 | * [Code coverage](https://github.com/dimsav/phpspec-reference/wiki/PhpSpec:-Code-coverage-plugin) 327 | 328 | ## Templates 329 | 330 | Custom templates can be used when phpspec generates php code. There are three template types, each of which, requires a different file name: 331 | 332 | * Specs: [specification.tpl](#specification-template) 333 | * Classes: [class.tpl](#class-template) 334 | * Methods: [method.tpl](#method-template) 335 | 336 | Phpspec will look for template files in these locations with the following order: 337 | 338 | 1. `{project_directory}/.phpspec/` 339 | 2. `{user_home_directory}/.phpspec/` 340 | 341 | To see the available template parameters visit the [parameters chapter](http://www.phpspec.net/cookbook/templates.html#parameters) in the docs. 342 | 343 | #### Specification template 344 | 345 | ``` 346 | Required parameter $ missing 388 | 389 | To fix that, I edit the docblock of the ObjectBehavior.php file in the vendor directory. 390 | 391 | ```php 392 | /* 393 | * @method void beConstructedWith($constructorArguments,...) 394 | */ 395 | 396 | // change to 397 | 398 | /* 399 | * @method void beConstructedWith($constructorArguments) 400 | */ 401 | ``` 402 | 403 | ### Expected ... (?) 404 | 405 | When we expect some string to be returned, we need more details about the differences of the output and the expected string. 406 | 407 | > expected "some long string that is ...", but got "some different long strin...". 408 | 409 | This message isn't very helpful. To get the full difference in the string, plus lots of other information, pass the `-v` option: 410 | 411 | `phpspec run -v` 412 | 413 | will now output something like 414 | 415 | ``` 416 | 25 ✘ should match strings 417 | expected "some long string that is ...", but got "some different long strin...". 418 | 419 | @@ -1,1 +1,1 @@ 420 | -some long string that is really long. 421 | +some different long string that is really long. 422 | 423 | 424 | 25 function it_should_match_strings() 425 | 26 { 426 | 27 $this->foo()->shouldReturn('some long string that is really long.'); 427 | 28 } 428 | 29 429 | 430 | 431 | 0 vendor/phpspec/phpspec/src/PhpSpec/Matcher/IdentityMatcher.php:78 432 | throw new PhpSpec\Exception\Example\NotEqualException("Expected "some lon...") 433 | 1 [internal] 434 | Spec\Foo\StringSpec->it_should_match() 435 | ``` 436 | 437 | This also works with objects: 438 | 439 | ``` 440 | @@ -1,4 +1,4 @@ 441 | -stdClass Object &000000000f3eb7a000000000285a17d7 ( 442 | - 'foo' => 'bar' 443 | +stdClass Object &000000000f3eb78e00000000285a17d7 ( 444 | + 'bax' => 'bar' 445 | 'bar' => 'foo' 446 | ) 447 | ``` 448 | 449 | and arrays: 450 | 451 | ``` 452 | @@ -1,4 +1,4 @@ 453 | [ 454 | far => ""foo"...", 455 | - boo => ""baz"...", 456 | + boo => ""bar"...", 457 | ] 458 | ``` 459 | 460 | ## Sources 461 | 462 | * https://github.com/phpspec/phpspec-docs/blob/master/en/writing-specs.rst 463 | * http://www.slideshare.net/marcello.duarte/phpspec-bdd-for-php 464 | * https://github.com/yvoyer/phpspec-cheat-sheet 465 | --------------------------------------------------------------------------------