How to test procedural functions with PHPUnit

Published on 2021-03-30. Modified on 2021-03-31.

Some people argue that you cannot test procedural PHP code, but that is not true. In this small tutorial I am going to show you how you can use PHPUnit to test your procedural functions.

Take a look at the documentation for relevant information about how to use PHPUnit.

All official releases of code distributed by the PHPUnit Project are signed by the release manager for the release. PGP signatures and SHA256 hashes are available for verification on phar.phpunit.de

In this simple example I am going to use the phar version of PHPUnit.

We start by downloading phpunit.phar as well as its detached PGP signature phpunit.phar.asc:

$ wget https://phar.phpunit.de/phpunit-9.5.phar
$ wget https://phar.phpunit.de/phpunit-9.5.phar.asc

In order to proceed with the verification we need to retrieve the release manager's public key from a key server:

$ curl --silent https://sebastian-bergmann.de/gpg.asc | gpg --import

Then we verify the signature:

$ gpg phpunit-9.5.phar.asc
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: assuming signed data in 'phpunit-9.5.phar'
gpg: Signature made tir 23 mar 2021 08:16:55 CET
gpg:                using RSA key D8406D0D82947747293778314AA394086372C20A
gpg:                issuer "sb@sebastian-bergmann.de"
gpg: Good signature from "Sebastian Bergmann " [unknown]
gpg:                 aka "Sebastian Bergmann " [unknown]
gpg:                 aka "Sebastian Bergmann " [unknown]
gpg:                 aka "Sebastian Bergmann " [unknown]
gpg:                 aka "Sebastian Bergmann " [unknown]
gpg:                 aka "[jpeg image of size 40635]" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: D840 6D0D 8294 7747 2937  7831 4AA3 9408 6372 C20A

The signature is good and a good signature means that the file has not been tampered with. However, due to the nature of public key cryptography, you need to additionally verify that the primary key fingerprint was created by the real Sebastian Bergmann. I am going to leave that up to you to figure out :)

Then we make the script executable:

$ chmod +x phpunit-9.5.phar
$ ./phpunit-9.5.phar --version
PHPUnit x.y.z by Sebastian Bergmann and contributors

Now we're ready to do some testing.

Let's create a single file in our public directory and put a couple of functions into it:

$ vim public/foo.php

<?php
declare(strict_types=1);

function bar(string $string): string {
    return $string;
}

function add(int $a, int $b): int {
    return $a + $b;
}

Then we create a test file for foo.php:

$ vim test_foo.php

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

// We require the file we need to test.
require 'public/foo.php';

final class test_foo extends TestCase
{
    public function test_bar() {
        $this->assertEquals('Hello', bar('Hello'));
        $this->assertEquals('Hi', bar('Hi'));
    }

    public function test_add() {
        $this->assertEquals(4, add(1, 3));
        $this->assertEquals(10, add(4, 6));
    }
}

Make sure that the class name you use match the actual filename of the test file.

The run the test:

$ ./phpunit-9.5.phar --colors test_foo.php
PHPUnit 9.5.4 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 00:00.020, Memory: 18.00 MB

OK (2 tests, 4 assertions)

Let's make the test fail by changing the assertion test for the add function. Change it to something that is wrong:

$this->assertEquals(15, add(4, 6));

This should fail because 4 + 6 is not 15:

$ ./phpunit-9.5.phar --colors test_foo.php
PHPUnit 9.5.4 by Sebastian Bergmann and contributors.

.F                                                                  2 / 2 (100%)

Time: 00:00.019, Memory: 18.00 MB

There was 1 failure:

1) test_foo::test_add
Failed asserting that 10 matches expected 15.

test_foo.php:15

FAILURES!
Tests: 2, Assertions: 4, Failures: 1.

That's pretty much it. Take a look at the documentation for relevant information about other kinds of tests.