• Stars
    star
    145
  • Rank 254,144 (Top 6 %)
  • Language
  • Created over 10 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A guide containing phpspec use cases

PHPSpec reference guide

A guide containing phpspec snippets for different use cases

Installation

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'

Commands

Describe

phpspec describe Namespaced/Class/Name

Describe creates the spec file for the class you want to specify.

Run

phpspec run

Run executes the specs and ends with a nice output.

PhpSpec output

Mocks

Creating mocks

Injecting mocks in constructor

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()
}

Injecting mocks in spec

function it_sends_the_message(Acme\Dependency $dependency)
{
    // $dependency is now a new mock of Acme\Dependency
}

Using mocks

Returning values from mock methods

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]);
}

Specify that a mock method should be called

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);
}

Exceptions

Throw exception

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');
}

Throw exception with message

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');
}

Throw exception in constructor

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'));
}

Matching types

Identical

$this->method()->shouldBe($result);

// Alternative syntax
$this->method()->shouldReturn($result);
$this->method()->shouldEqual($result);
$this->method()->shouldBeEqualTo($result);

Equal

$this->method()->shouldBeLike($result)

Data types

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();

Objects

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');

Count

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);
    }
}

Dynamic matching types

Boolean shouldBe*

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();

Boolean shouldHave*

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

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()

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;
        }
    );
}

Using getMatchers()

To use the above expectation we use the method should + {getMatchers key}.

$this->method()->shouldHaveLength(12);

Plugins

Templates

Custom templates can be used when phpspec generates php code. There are three template types, each of which, requires a different file name:

Phpspec will look for template files in these locations with the following order:

  1. {project_directory}/.phpspec/
  2. {user_home_directory}/.phpspec/

To see the available template parameters visit the parameters chapter in the docs.

Specification template

<?php namespace %namespace%;

use PhpSpec\ObjectBehavior,
    Prophecy\Argument;

class %name% extends ObjectBehavior
{
    function let()
    {
        
    }

}

Class template

<?php namespace %namespace%;

class %name%
{
    
}

Method template

public function %name%(%arguments%)
{
    
}

Tips

PhpStorm

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)
 */

Expected ... (?)

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"...",
  ]

Sources