TL;DR: In this post, I cover PHPUnit, PHP, phpunit.xml, unit testing, and macOS and how to get them all to work together despite what ships with macOS.


In the initial draft of this post, I wrote about how I was going to start writing unit tests for small pieces of functionality as well as show exactly what functionality I was writing (and the why behind it).

But this took a weird turn that that lead me on a long digression into three things:

  1. PHPUnit 9,
  2. the version of PHP that ships with macOS,
  3. And how to get them to work together nicely.

So rather than try to cover all of that in a single post, I thought it better to talk about PHPUnit, PHP, phpunit.xml, and macOS in a single post, then get back to to the practical work of building the project.

Building Backcast, Unit Testing

When I started working on the feature necessary to begin backing up the podcasts from the provided XML file, I found that I wanted to verify the XML file being provided looked enough like an Overcast export to get started with unit testing.

This lead me into something I’ve done over and over throughout the years:

  • install PHPUnit,
  • setup a configuration file,
  • and then write tests

But when it comes to doing this on macOS, specifically if you’re doing this with the version of PHP that ships with the operating system, there are going to be problems.

So, first, I’ll share my problems then how I resolved them.

Unit Testing Set Backs

PHP is Deprecated in Big Sur

It’s not really so much deprecated as it’s going to be removed from future versions of the operating system. There’s a lot of information about this on the web that can be found (like here on Hacker News) but the point remains: PHP will eventually be going away and will need to be manually installed.

This isn’t the biggest problem, though.

Setting Up

I go into my project and I create the following composer.json file:

{
  "name": "tommcfarlin/backcast",
  "description": "An application used to backup podcast subscriptions via Overcast XML exports.",
  "require-dev": {
    "phpunit/phpunit": "^8"
  },
  "autoload": {
    "psr-4": {
      "Backcast\\": "src"
    }
  },
  "autoload-dev": {
    "psr-4": {
        "Backcast\\Tests\\": "tests"
    }
  }
}

Then I set up a tests directory that includes a SampleTest.php file. For those interested, it looks like this:

<?php

namespace Backcast\Tests;

use PHPUnit\Framework\TestCase;

class SampleTest extends TestCase
{
  public function testSameString(): void
  {
    $this->assertSame('sameString', 'sameString');
  }

  public function testDifferentString(): void
  {
    $this->assertNotSame('stringOne', 'stringTwo');
  }
}

It’s easy enough to run this code from the command-line by running the following:

$ vendor/bin/phpunit tests/SampleTest.php

But I want to automate this and future tests. To do that, I set up a basic phpunit.xml configuration file so I can separate things into test suites (or a single one, really, at this point) and then I can just let PHPUnit run and do its thing by reading said file.

Configuration Problems

So I have my xml file configuration set up and then I go to run it locally:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
>
  <testsuites>
    <testsuite name="Tests">
        <directory suffix="Test.php">./tests</directory>
    </testsuite>
  </testsuites>
</phpunit>

But it reports that zero tests are run. What? I literally ran the tests manually and the file is directing PHPUnit as to where my tests are.

So I try once more. And still the same. I’ve literally never seen this happen before.

At this point, I run php -v from the command-line to start seeing if I can figure out what the problem is (because it doesn’t appear to be anything installed via Composer nor does it seem to be anything set up in my project at all).

When I check the version, I’m given this message:

7.3.22-(to be removed in future macOS)

Wait. That’s a weird string for a -v to return. 🤔 I wonder if that’s throwing something off in PHPUnit.

But you know what? PHPUnit is an open source project so I go to the project on GitHub and – rather than spend a ridiculous amount of time walking through the number of things I combed over to find this – I’ll share what I found:

I notice version_compare:

Compares two “PHP-standardized” version number strings

PHP Manual

So if the version_compare function isn’t working like it’s expected then maybe it’s because it has to do with the version number string (which may be, in turn, that’s why PHPUnit isn’t honoring what I’ve put into the XML file). More speficially, if it’s expecting a version number to actually compare but it’s getting the following:

7.3.22-(to be removed in future macOS)

How could it actually work? This isn’t a standardized version string. It’s not even a version. Strictly speaking, you can’t accurately do a version compare between a non-standard version string and a standard version string. 🤦🏻‍♂️

So rather than roll with a standard version of PHP, I decide that I might as well upgrade PHP anyway.

Installing PHP 7.4

Since I’m a fan of Homebrew, I’m going to use it to install PHP 7.4. I’ve already written about using Homebrew to run multiple versions of PHP in the past so I’m not going to belabor the point. You can read the article.

In short, I do the following:

  • Update Homebrew,
  • Clean up anything, like symlinks, that may be dead,
  • Install PHP 7.4.15
  • Then set it as the default version.

After that, I can run which to verify that everything is working as expected:

And it is. I have PHP 7.4.15 running. And with that, I run PHPUnit from within my project’s directory.

It works.

A Lot of Work, A Little Reward?

This felt like an inordinate amount of work for a little reward. And it was an inordinate amount of work just get PHPUnit set up.

In terms of the reward, though, it definitely doesn’t feel like much right now. I mean all I can do, at the time of writing this post, is run a couple of of fake unit tests. But now that I’ve got this particular part sorted out, I’m in a good position to write, test, and write some more.

So it’s a little reward for now but a lot to come in the future. And if this single post, out of this entire series, helps someone with the same problem, it’ll also be worth it.

Scattered Thoughts

  • I was actually able to upgrade to PHPUnit 9 after all. There must’ve been a problem with packagist when I attempted to do so in the first post. I’ll document the change in the next post.
  • I didn’t anticipate having to split up posts before I actually have anything to show but such is the nature of what I’m trying to do with this whole thing.
  • I’ve got notes for the first few episodes of the podcast so I’m looking to get that off the ground within the next week or so. This particular post, if it wasn’t evident enough, already set me back more than I expected. I wanted to understand the why behind all of this. So there we are.