A guide containing phpspec snippets for different use cases
Install with composer using this composer command:
composer require phpspec/phpspec:2.*@dev --dev
Or by adding the depencency manually to your composer.json file:
{
"require-dev": {
"phpspec/phpspec": "2.0.*@dev"
}
}
Optionally, add the following aliases to your system:
# PHPSpec Command
alias phpspec='vendor/bin/phpspec'
# PHPSpec Shortcuts
alias spec='vendor/bin/phpspec'
alias specr='vendor/bin/phpspec run'
alias specd='vendor/bin/phpspec describe'
phpspec describe Namespaced/Class/Name
Describe creates the spec file for the class you want to specify.
phpspec run
Run executes the specs and ends with a nice output.
Inject a mock in the constructor for use in your specs:
// spec/Acme/HandlerSpec.php
function let(Acme\Dependency $dependency)
{
$this->beConstructedWith($dependency);
}
// Alternative syntax
function let($dependency)
{
$dependency->beADoubleOf('Acme\Dependency');
$this->beConstructedWith($dependency);
}
Pass in the spec the same mock variable name:
// Our spec
function it_sends_the_message($dependency)
{
// $dependency is now the mock we created in let()
}
function it_sends_the_message(Acme\Dependency $dependency)
{
// $dependency is now a new mock of Acme\Dependency
}
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.
function it_sends_the_message(Acme\Message $message)
{
$message->getContents()->willReturn('abc');
$this->send($message);
}
Now imagine you need to call a method several times and have a diferent value each time.
function it_gets_three_random_numbers(Acme\RandomGenerator $rand)
{
$rand->generate()->willReturn(123, 432, 874);
$this->getNumbers($rand, 3)->shouldReturn([123, 432, 874]);
}
Usually we want to make sure that a mock method is executed. We do that with shouldBeCalled()
.
function it_sends_the_message(Acme\Message $message)
{
$message->getContents()->shouldBeCalled()->willReturn('abc');
$this->send($message);
}
Expect an InvalidArgumentException
to be thrown when we call "send('bad')". Passing the exception type as string prevents the message validation.
function it_throws_exception_during_send(Acme\Message $message)
{
$this->shouldThrow('\InvalidArgumentException')->duringSend('bad');
}
Expect an InvalidArgumentException
with message "Wrong argument" to be thrown when we call send('bad')
:
function it_throws_exception_during_send(Acme\Message $message)
{
$exception = new InvalidArgumentException('Wrong argument');
$this->shouldThrow($exception)->duringSend('bad');
}
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.
function it_throws_exception_during_constructor(Acme\Message $message)
{
$this->shouldThrow(new \InvalidArgumentException)->during('__construct', array('bad1', 'bad2'));
}
$this->method()->shouldBe($result);
// Alternative syntax
$this->method()->shouldReturn($result);
$this->method()->shouldEqual($result);
$this->method()->shouldBeEqualTo($result);
$this->method()->shouldBeLike($result)
Expect the result of a method has a specific data type.
$this->method()->shouldBeBool();
$this->method()->shouldBeObject();
$this->method()->shouldBeString();
$this->method()->shouldBeInteger();
$this->method()->shouldBeDecimal();
$this->method()->shouldBeArray();
All the following syntaxes expect that method() returns an instance of the given class.
$this->method()->shouldHaveType('\Full\Class\Name');
$this->method()->shouldReturnAnInstanceOf('\Full\Class\Name');
$this->method()->shouldBeAnInstanceOf('\Full\Class\Name');
$this->method()->shouldImplement('\Full\Class\Name');
You can use shouldHaveCount()
on a method returning an array
or an instance of \Countable
.
$this->getArray()->shouldHaveCount(2)
$this->getCollection()->shouldHaveCount(2)
public function getArray() {
return array(1, 2)
}
public function getCollection() {
return $this->users;
}
You can also use shouldHaveCount()
on $this
, if it is an instance of \Countable
.
$this->shouldHaveCount(10);
class UsersCollection implements \Countable {
public function count() {
return count($this->users);
}
}
To use the shouldBe* matching, the class should have a public method starting with "is".
// User.php
public function isActive() {
return (bool) $this->isActive;
}
// expect isActive returns true
$this->shouldBeActive();
// expect isActive returns false
$this->shouldNotBeActive();
To use the shouldHave* matching, the class should have a public method starting with "has".
// User.php
public function hasProfile() {
return (bool) $this->hasProfile;
}
// expect hasProfile returns true
$this->shouldHaveProfile();
// expect hasProfile returns false
$this->shouldNotHaveProfile();
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.
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.
function getMatchers()
{
return array(
'haveLength' => function($result, $count) {
return strlen($result) == $count;
}
);
}
To use the above expectation we use the method should
+ {getMatchers key}
.
$this->method()->shouldHaveLength(12);
Custom templates can be used when phpspec generates php code. There are three template types, each of which, requires a different file name:
- Specs: specification.tpl
- Classes: class.tpl
- Methods: method.tpl
Phpspec will look for template files in these locations with the following order:
{project_directory}/.phpspec/
{user_home_directory}/.phpspec/
To see the available template parameters visit the parameters chapter in the docs.
<?php namespace %namespace%;
use PhpSpec\ObjectBehavior,
Prophecy\Argument;
class %name% extends ObjectBehavior
{
function let()
{
}
}
<?php namespace %namespace%;
class %name%
{
}
public function %name%(%arguments%)
{
}
If you use PhpStorm, there is one incompatibility in the docs of beConstructedWith()
, when only one parameter is given.
Required parameter $ missing
To fix that, I edit the docblock of the ObjectBehavior.php file in the vendor directory.
/*
* @method void beConstructedWith($constructorArguments,...)
*/
// change to
/*
* @method void beConstructedWith($constructorArguments)
*/
When we expect some string to be returned, we need more details about the differences of the output and the expected string.
expected "some long string that is ...", but got "some different long strin...".
This message isn't very helpful. To get the full difference in the string, plus lots of other information, pass the -v
option:
phpspec run -v
will now output something like
25 ✘ should match strings
expected "some long string that is ...", but got "some different long strin...".
@@ -1,1 +1,1 @@
-some long string that is really long.
+some different long string that is really long.
25 function it_should_match_strings()
26 {
27 $this->foo()->shouldReturn('some long string that is really long.');
28 }
29
0 vendor/phpspec/phpspec/src/PhpSpec/Matcher/IdentityMatcher.php:78
throw new PhpSpec\Exception\Example\NotEqualException("Expected "some lon...")
1 [internal]
Spec\Foo\StringSpec->it_should_match()
This also works with objects:
@@ -1,4 +1,4 @@
-stdClass Object &000000000f3eb7a000000000285a17d7 (
- 'foo' => 'bar'
+stdClass Object &000000000f3eb78e00000000285a17d7 (
+ 'bax' => 'bar'
'bar' => 'foo'
)
and arrays:
@@ -1,4 +1,4 @@
[
far => ""foo"...",
- boo => ""baz"...",
+ boo => ""bar"...",
]