---
title: BDD basics with PHPSpec
description: In this post I'll be covering the basics of the PHPSpec testing tool,
  so you can get started with Behaviour Driven Development!
date: '2020-06-17T17:30:00+00:00'
tags:
- bdd
- testing
- phpspec
url: https://joppe.dev/2020/06/17/behaviour-driven-development-basics/
---

> BDD is a technique used at story level and spec level. The technique is to first describe the behaviour of an object you are about to write. Next you write just enough code to meet that specification.
>
> <cite><a href="https://www.phpspec.net/" target="_blank">PHPSpec.net</a></cite>

Sounds interesting. Let's give it a shot, shall we?

## Setup
We're going to start off creating a fresh project using the <a href="https://symfony.com/doc/current/setup.html" target="_blank">Symfony Skeleton</a>, and installing PHPSpec.
Using the symfony binary, creating a new project is very straight forward. You can follow along if you like.

```console
symfony new myproject
cd myproject
composer require phpspec/phpspec
```

## Motorcycles

I like motorcycles, so let's use that as our subject.

First of all, we'll need to create the specification to describe our motorcycle class.
Luckily, PHPSpec can help us create this file.

```console
vendor/bin/phpspec describe App/Motorcycle
```

PHPSpec will now generate a specification for the `App/Motorcycle` class in the `tests/spec` directory.
The generated file will look like this:

```php
<?php

namespace spec\App;

use App\Motorcycle;
use PhpSpec\ObjectBehavior;

class MotorcycleSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType(Motorcycle::class);
    }
}
```

As you can see, this doesn't describe much. The method `it_is_initializable` will just check if the Motorcycle class can be initialized. Currently, we haven't created that class yet - so running PHPSpec should give us an error.
We can run the tests using the following command.

```console
vendor/bin/phpspec run
```

![PHPSPec Console Results]({{site.baseurl}}/images/posts/phpspec/1.png)

As expected, PHPSpec tells us our code is broken. It also suggests creating the class for us, so we'll accept that offer.
_Note: You can disable the suggestions by passing the `--no-code-generation` option when running the command._
We've now got our base class:

```php
<?php

namespace App;

class Motorcycle
{
}
```

This also means our test should pass, so let's try!

![PHPSPec Console Results]({{site.baseurl}}/images/posts/phpspec/2.png)

Great. As you noticed, PHPSpec helped us out quite a bit here. As you'll see in the rest of this post, PHPSpec can actually generate quite a lot of stuff to help us.
Let's start adding some methods to our Motorcycle class.

## First custom behaviour

We're going to start off with a simple behaviour.
For example, we want to make sure our motorcycle is currently running. We can add a fairly simple test for that to our specification.

```php
class MotorcycleSpec extends ObjectBehavior
{
    //...

    function it_is_running(){
        $this->isRunning()->shouldReturn(true);
    }
}
```

Strange. We're calling the `isRunning()` method, but it doesn't exist in our specification. Surprise! There's a catch. The `$this` keyword in the specification doesn't refer to the spec itself, but to our Motorcycle class. Therefore, PHPSpec will try calling the function on the Motorcycle class.

We're also chaining the `shouldReturn` function. This is a <a href="https://www.phpspec.net/en/stable/cookbook/matchers.html" target="_blank">PHPSpec Matcher</a>. Matchers in PHPSpec are similar to assertions in PHPunit, and describe how an object should behave. There's a lot of matchers available, to _match_ your usecase.

We can try running our test again.

![PHPSPec Console Results]({{site.baseurl}}/images/posts/phpspec/3.png)

The failing test is obviously the `it_is_running` test.
All tests green looked way better, so let's fix it.
In our Motorcycle class, we'll add a method `isRunning()` that returns true.

```php
class Motorcycle
{
    public function isRunning(): bool
    {
        return true;
    }
}
```

This immediately fixes our tests, as expected.

![PHPSPec Console Results]({{site.baseurl}}/images/posts/phpspec/4.png)

## Slightly more advanced testing (Stubs)

Class methods often require arguments to perform actions. Our motorcycle, for example, could have a function `canRide(Rider $rider)`, that returns whether or not the rider can ride the motorcycle.
To test this, we'll add the following stub:

```php
//...
use App\Rider;
//...
class MotorcycleSpec extends ObjectBehavior
{
    //...

    function it_can_be_ridden_by_a_rider(Rider $rider)
    {
        $rider->hasMotorcycleLicense()->willReturn(true);
        $this->canRide($rider)->shouldReturn(true);
    }
}
```

![PHPSPec Console Results]({{site.baseurl}}/images/posts/phpspec/5.png)

Our tests are failing, but as you can see, PHPSpec suggests generating an interface `App\Rider`.
Perhaps we'll want to add different types of people later, so using an interface makes sense. Let's say no for now, and replace our Rider typehint with PersonInterface. This way we can make sure our naming is nice and clean.

We'll run the PHPSpec command again, and allow it to generate the interface for us.
Okay, so now that we've got the interface, will our test pass?

![PHPSPec Console Results]({{site.baseurl}}/images/posts/phpspec/6.png)

Obviously not. Our interface doesn't even have the `hasMotorcycleLicense()` method signature.
Again, PHPSpec suggests generating it, so let's agree.

Sweet! We've now got our interface ready.
Note that in the code snippet below, I did add the `bool` return type myself.

```php
//...

interface PersonInterface
{
    public function hasMotorcycleLicense(): bool;
}
```

All we need to do now, is implement the `canRide` method in the Motorcycle class.

```php
class Motorcycle
{
    //...

    public function canRide(PersonInterface $person): bool
    {
        return $person->hasMotorcycleLicense();
    }
}
```

When we try running PHPSpec again, we can once again see that all our tests are green!

![PHPSPec Console Results]({{site.baseurl}}/images/posts/phpspec/7.png)

## Conclusion

The functionalities I've shown here are just the tip of the iceberg.
Perhaps I can do a follow-up with some more advanced examples, but for now, this is the end.
I hope you've enjoyed this read, and hopefully you've even learned something along the way. I highly suggest that you check out the <a href="http://www.phpspec.net/en/stable/manual/introduction.html" target="_blank">PHPSpec Manual</a> if you're interested in learning more.

### Sources

* <a href="http://www.phpspec.net/en/stable/manual/introduction.html" target="_blank">PHPSpec Documentation</a>
* <a href="https://github.com/phpspec" target="_blank">PHPSpec Github</a>
* <a href="https://symfonycasts.com/screencast/phpspec" target="_blank">SymfonyCasts</a>

### Technologies Used

* Symfony 5.1
* PHPSpec 6.2
