Ask me about Unit Testing and I’ll probably say, “nice to have”. Unit tests are very nice to have … but sometimes the project schedule doesn’t include time to build the unit tests — which often take nearly as much time as writing the functional code. But when I have time, I really like good unit tests.

I’ve never found a PHP unit test framework that I like, so I roll my own. It’s evolved over time from “call functions and see if they fail” to “test all scenarios and track complete coverage”. Here’s the basic framework I use:

  1. All PHP files can conduct their own unit tests by simply executing them from the command line, e.g.
    • ]# php Project.php
  2. A script will run all unit tests
    • ]# ./unitTests.sh
  3. All PHP functions declare their usage so that coverage can be tracked

A “hello world” class of mine looks something like this:

require_once('unitTests.php');
class HelloWorld {
  function findWorld() {
    unitTestCoverage(__METHOD__);
    return "Earth";
  }
  function findGalaxy() {
    unitTestCoverage(__METHOD__);
    return "Milky Way";
  }
}
// Unit Tests
doUnitTests_HelloWorld();
function doUnitTests_HelloWorld() {
  // Only run tests when executed from the command line
  if (!(php_sapi_name() == "cli")) return;
  // Only run tests when it's THIS file that's executed from the command line
  global $argv;
  if (pathinfo(__FILE__, PATHINFO_FILENAME) != pathinfo($argv[0],PATHINFO_FILENAME)) return;
  // Create coverage arrays
  global $__localAllCoverage, $__localCoverage;
  $__localAllCoverage = [
    "HelloWorld::findWorld"
    ,"HelloWorld::findGalaxy"
  ];
  $__localCoverage = [];

  echo "HelloWorld.php unit tests\n";

  $msg = "findWorld test";
  $hw = new HelloWorld();
  if ($hw->findWorld() != 'Earth') {
    unitTestFailed($msg,[$hw]);
  } else {
    echo "Success $msg\n";
  }

  $msg = "findGalaxy test";
  if ($hw->findGalaxy() != 'Milky Way') {
    unitTestFailed($msg,[$hw]);
  } else {
    echo "Success $msg\n";
  }

  $diff = array_diff($__localAllCoverage,$__localCoverage);
  if (count($diff) != 0) {
    unitTestFailed('Tests did not have complete coverate.',[$__localAllCoverage,$__localCoverage,$diff]);
  }

  echo "\n\033[32m Success! \033[0m \n";
  exit(0);
}

And the unit test functions in unitTests.php look like this:

function unitTestCoverage($__f) {
  if (!(php_sapi_name() == "cli")) return;

  // Running unit tests!
  global $__localCoverage;

  if (!in_array($__f,$__localCoverage)) {
    array_push($__localCoverage,$__f);
    echo " .. {\033[94m" . end($__localCoverage) . "\033[0m} .. ";
  }
}
function unitTestFailed($msg,$data) {
  foreach ($data as $d) {
    print_r($d);
  }
  echo "\n \033[31m FAILED $msg \033[0m \n";
  exit(1);
}

In my script that runs every unit test, I check for the failure exit codes:

#/bash/bin

cd www/api/classes

files[0]=HelloWorld.php

for i in "${files[@]}"
do
  php $i
  if [ $? -ne 0 ]
  then
    echo -e "\n\nFAILURE in ${i}\n"
    exit 1
  fi
done

echo -e "\n\nALL TESTS PASSED!\n"

exit 0

I hope you like this framework as much as I do! It has ensured full unit test coverage and cut way down on bugs that get to a test or production stage.

One Response to “PHP Unit Testing”

  • Oliver Russell says:

    Have you tried PHPunit for automating unit testing? This is a really good tool. I recommend you to use it. If you are using this tool, you only have to write your test case and then run the PHP unit testing using single composer command. Saves a lot of your time.

Leave a Reply