Symfony Unit Testing Part 1 – Getting started

If you are reading this then you understand that unit testing is important. We can manually test if an application works. But this is not always as accurate as we want. And this can also be time consuming. We want to have tests that can be run automatic now and in the future. On of the tools we can use is PHPUnit.

With this we can test the units of our system . These tests show other developers how the system works. It define’s and validate’s the uses cases.

We can adopt a test-first or test-last approach. Whatever approach you use will not be important for this series of blog posts. What is important; is how get started in Symfony.

Setup

First I assume you have a basic knowledge of Symfony 4 and are starting from the Symfony skeleton application.

composer create-project symfony/skeleton symfony4-unittesting

The next step is to get your development environment running. You can use whatever you want. I will be using a docker container to run the tests in. This is a container I use for most of my development and is based on a generated docker project from phpdocker. You will then need to run all future commands in the php-fpm container.

Then the next step is to install PHPUnit. Symfony has a PHP Unit bridge bundle. This just wraps some additional functionality for compatibility around PHP Unit.

composer require --dev symfony/phpunit-bridge

You can find the the skeleton application with the docker image and PHP Unit installed here.

Running PHPUnit

After you have setup your symfony project and development environment. Then executed composer update. The next step is to run PHPUnit once to install its dependencies and see if it works.

./vendor/bin/simple-phpunit

If everything is fine you will see No tests executed!
This means its time to create our first test.

The first Test

Creating our first test is easy. All our tests need to be placed inside the tests directory. The name of the class should end with Test and it should extend from PHPUnit\Framework\TestCase or any of its subclassesAnd each testing method inside the class should be public and start with test.

So lets create a simple NumberMultiplier class. We will be using a test-first approach. This means we first create a new class called NumberMultiplierTest and then extend from a PHPUnit TestCase.

<?php
/** tests/Service/NumberMultiplierTest.php */
namespace App\Tests\Service;


use PHPUnit\Framework\TestCase;

class NumberMultiplierTest extends TestCase
{

}

Now we can run this empty test by providing the file name as a parameter for our PHPUnit executable.

./vendor/bin/simple-phpunit tests/Service/NumberMultiplierTest.php

This explicitly tells PHPUnit to only run that test. You can also run all tests in a given directory or just run all tests.

Well this is kind of a useless test. It will probably return No tests found in class “App\Tests\Service\NumberMultiplierTest”. This is a warning. It tells you that you probably need to write some actual tests.

The first actual Tests

The first actual test is to see if we can instantiate our NumberMultiplier class.

public function testCreateNewNumberMultiplier(): void
{
    $numberMultiplier = new NumberMultiplier();
    $this->assertInstanceOf(NumberMultiplier::class, $numberMultiplier);
}

I know this test will fail. We haven’t created this class yet. How could it work?
So we fix this by creating the class and then run the test again.

<?php
/** src/Service/NumberMultiplier.php */
namespace App\Service;


final class NumberMultiplier
{

}

Now we can start with our user stories. From these we learn that every number needs to be positive. We need a method to check if its positive. So we can create the next test to validate that we have a method doing this.

public function testPositiveNumberIsPositive(): void
{
    $numberMultiplier = new NumberMultiplier();
    $positiveNumber = $numberMultiplier->isNumberPositive(5);
    $this->assertTrue($positiveNumber);
}

And with this we create the method that validates this test.

<?php
/** src/Service/NumberMultiplier.php */
namespace App\Service;


final class NumberMultiplier
{

    public function isNumberPositive(int $number): bool
    {
        return $number > 0;
    }
}

But what if we provide a negative number, will this return false? Well we should also write a test for this, just to be sure it will keep working whenever someone refactors this code.

public function testNegativeNumberIsNotPositive(): void
{
    $numberMultiplier = new NumberMultiplier();
    $negativeNumber = $numberMultiplier->isNumberPositive(-1);
    $this->assertFalse($negativeNumber);
}

I think you are getting the idea now.

Whats next?

You have learned to run tests and created a simple class in a test-first approach. And you know how to use the basic assert methods. To learn more about more advanced assertions you can read the official documentation here.

In part 2 we will learn more about using test doubles.