The Basic Workflow of Writing Unit Tests with PHPUnit

So, you now know the basic concepts of testing, and how to use PHPUnit as a tool. Now, let's see the basic workflow of writing unit tests with PHPUnit with a simple example.

What are we going to build?

I couldn't find an easier example than a calculator — I want it to be so easy so I can focus more on the workflow itself rather than other unrelated things.

To give you a quick idea of what we're going to build, take a look at this code snippet.

$calculator = new Calculator(2);
$result = $calculator->add(3)->sub(1)->getResult();
// $result = 4

Setting up the environment

Let's do this so quickly since we've covered it in detail in the previous tutorial.

First, install PHPUnit via composer:

composer require phpunit/phpunit --dev

Then, create the source and tests directories (src & tests). After that, create CalculatorTest.php (in tests) and Calculator.php (in src).

Finally, define a PSR-4 autoloader in composer.json. Here's how it should look like (don't forget to run composer dumpautoload after that):

{
    "require-dev": {
        "phpunit/phpunit": "^4.8"
    },

    "autoload": {
        "psr-4": {
            "": "src/"
        }
    }
}

The Example

When the user instantiates a new instance of the calculator, he should be able to give it a number as a start — then we'll use this number as the first number in follow-up math operations (addition, subtraction, etc).

Since this tutorial isn't about TDD, I won't show you the complete workflow of it — I'll keep it for another tutorial. However, I find it easier to show you the test first, then the implementation (so you know how we're going to build it).

class CalculatorTest extends PHPUnit_Framework_TestCase
{
    /** @test */
    public function it_can_start_with_an_initial_number()
    {
        $calculator = new Calculator(8);

        $this->assertEquals(8, $calculator->getResult());
    }
}

So it's clear from this test that we're giving the calculator the number 8 as the initial number. Also, notice how we used the method getResult() to get the calculator's current result — in this case we didn't do anything other than pressing the number 8, so to speak.

To make this test pass, here's what you have to put in Calculator.php:

class Calculator
{
    private $result;

    function __construct($initial)
    {
        $this->result = $initial;
    }

    public function getResult()
    {
        return $this->result;
    }
}

Run phpunit to see if it passes.

Our next test is to make sure that the initial value would default to 0 in case the user has not specified it.

/** @test */
public function it_starts_with_zero_if_nothing_is_specified()
{
    $calculator = new Calculator;

    $this->assertEquals(0, $calculator->getResult());
}

It will fail if you run it. To make it pass, all you have to do is to make the parameter of the constructor optional and give it 0 as a default:

function __construct($initial = 0)
{
    $this->result = $initial;
}

That's it!

Now, let's give our calculator the ability to add numbers. This is how it should work (the test):

/** @test */
public function it_adds_numbers()
{
    $calculator = new Calculator(2);

    $result = $calculator->add(3)->getResult();

    $this->assertEquals(5, $result);
}

So, we would need to create the add() method. And in that method we'd add the passed number to the $result property.

public function add($number)
{
    $this->result += $number;

    return $this;
}

Also, note how we returned $this from it. We did that to be able to chain other methods after calling it — this is called a fluent interface.

Let's do the subtraction now. Here's the test:

/** @test */
public function it_subtracts_numbers()
{
    $calculator = new Calculator(10);

    $result = $calculator->sub(2)->getResult();

    $this->assertEquals(8, $result);
}

The implementation:

public function sub($number)
{
    $this->result -= $number;

    return $this;
}

Let's finish up with this test:

/** @test */
public function it_can_mix_operations()
{
    $calculator = new Calculator(10);

    $result = $calculator->add(2)->sub(5)->getResult();

    $this->assertEquals(7, $result);
}

This one should work without doing anything. We sometimes write like these kind of tests to make sure that every aspect of our feature is working as expected. Also, it'll serve as a documentation for anyone who is new to the system.

The main goal

We can keep going forever with this example. But this isn't our point in this tutorial. Remember, the main goal of this tutorial is to get more familiar with the workflow of writing unit tests with PHPUnit.

You can get the source code from this gist.

Testing PHPUnit
Taha Shashtari

About Taha Shashtari

I'm a freelance web developer. Laravel & VueJS are my main tools these days and I love building stuff using them. I write constantly on this blog to share my knowledge and thoughts on things related to web development... Let's be friends on twitter.