Unit Testing with phpunit

In this post I’m going to explain and walk you through how to write unit tests with PHP using phpunit.

First step is to add php unit as a requirement in your composer.json file. You can do that by running the following command.

composer require --dev phpunit/phpunit

Let’s also go ahead and add an autoload entry to the composer file to make things easier, this is what the composer file should look like.

{
    "autoload": {
        "classmap": [
            "src/"
        ]
    },
    "require-dev": {
        "phpunit/phpunit": "^9.2"
    }
}

We also need to create two folders src/ and tests/ in the root directory where the composer.json file is.

Go ahead and create a file named PersonTest.php inside the tests/ directory.

<?php

use PHPUnit\Framework\TestCase;

class PersonTest extends TestCase {
    public function testConstruct(): void {
        $person = new Person();
    }
}

And another file named Person.php inside the src/ directory.

<?php

class Person {

}

Now let’s try to run the tests to check that the setup is working

./vendor/bin/phpunit tests

If you get an error like

There was 1 error:

1) PersonTest::testConstruct
Error: Class 'Person' not found

Then you may need to run this command.

composer dump-autoload 

And try running the tests again, now you should see something like

There was 1 risky test:

1) PersonTest::testConstruct
This test did not perform any assertions

Congrats! the tests are working now.

Now let’s go ahead and test something for real.

Add two assertions to the test that we have so it looks like this…

<?php

use PHPUnit\Framework\TestCase;

class PersonTest extends TestCase {
    public function testConstruct() {
        $person = new Person('Fernando', 'Castellanos');

        $this->assertEquals('Fernando', $person->firstName);
        $this->assertEquals('Castellanos', $person->lastName);
    }
}

What we’re doing here is adding a name and a last name to the Person’s construct and afterwards expecting that the person instance should have a firstName property with 'Fernando' as the value and a lastName property with 'Castellanos' as the value. What we’re really testing here is two things, one is that those two properties exist but also that their values are properly assigned at initialization.

Let’s get down to business and try running this test.

./vendor/bin/phpunit tests

The test should fail and we should see this error

1) PersonTest::testConstruct
Failed asserting that null matches expected 'Fernando'.

Well that seems obvious because we haven yet implemented anything and the Person class is empty. Let’s go ahead and create the __construct function.

<?php

class Person {
    public function __construct($firstName, $lastName) {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
}

Run the tests again…

PHPUnit 9.2.2 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 00:00.004, Memory: 4.00 MB

OK (1 test, 2 assertions)

Success!!! 🎉🎉🎉

Now in this example we used assertEquals from PHPUnit\Framework\TestCase there are several types of assertions that we can use, you can check them in the readthedocs page for phpunit here, but I’m still going to show you a few here.

Let’s say that we add two new properties to the Person class one named $dob for date of birth and another one $isProgrammer, for the $isProgrammer we’re going to assing it with true at class initialization.

<?php

class Person {
    public $dob;
    
    public function __construct($firstName, $lastName) {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->isProgrammer = true;
    }
}

Since we’re not assigning anything to $dob we should expect the property to be null while the $isProgrammer property to be true, let’s see how we would go about asserting those two new properties.

<?php

use PHPUnit\Framework\TestCase;

class PersonTest extends TestCase {
    public function testConstruct() {
        $person = new Person('Fernando', 'Castellanos');

        $this->assertEquals('Fernando', $person->firstName);
        $this->assertEquals('Castellanos', $person->lastName);
        $this->assertNull($person->dob);
        $this->assertTrue($person->isProgrammer);
    }
}

And that’s it! it’s pretty self-explanatory, assertNull asserts that the value that we are evaluating should be null and assertTrue asserts that the value that we’re evaluating should be true. With this you should be able to check the read the docs and be able to combile these and new assertions to make sure that your code is as bug free as possible.

If you have any comments or questions feel free to reach out on twitter at @fcastellanos.

Happy Coding!