Creating a Composer Library with PHPUnit and TDD

Spread the news

I recently decided to create a library that could handle asynchronous SQL queries and realized in the process that, while I am very comfortable using composer to install other people’s libraries I actually had no clue how to make and publish my own library!  I’m by far no expert on this, but let’s take a look at how I went about getting it set up and testing things.

Initial Setup

The first thing to do is to create a new project.  For me that meant making a new folder in my dev directory using mkdir library-name. After changing into the directory with cd library-name I could initialize my composer project. Running composer init starts an interactive shell that helps set up the basics.

Screenshot of *composer init*

You basically give your package a name, define the author, license, and package type. We’re creating a library that would be included and accessed from another project, so we should set the package type to library. For the license you can enter anything you want, but there are some recommended variants. The simplest license that lets people do anything they want except sue you is the MIT license. I generally use Apache-2.0 as it uses some additional legal jargon to protect the project from patent trolls, but MIT is said to be the most common open source license. You can find more information about this in Joseph Morris’s 2016 article Which License Should I Use? MIT vs. Apache vs. GPL.

If you don’t define any requirements interactively (you can – it will save you time overall), then you will end up with a composer.json file that looks something like this:

{
    "name": "johnfansler/library-name",
    "description": "My Test Library",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "John Fansler",
            "email": "engaged@engagedphp.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {}
}

Installing PHPUnit

This is great, but we now need to define a couple things. The first thing we will need is PHPUnit. This is a package that is required, but only for development, so when requiring it, we need to pass in the --dev argument: composer require phpunit/phpunit --dev. We then will need to set up the basic default PHPUnit settings. Let’s start by making a tests folder and then creating a basic phpunit.xml file like the following, but changing the testsuite name to your project name:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="vendor/autoload.php">
    <testsuites>
        <testsuite name="library-name">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>
</phpunit>

At this point we have PHPUnit ready to be used in our project, but no PSR-4 definition of the namespace! We need to edit the composer.json file to add in a reference to our future code. We will first have to create a src directory (or another name if you wish), and then determine the full namespace of the project. Generally speaking the namespace is in the format [vendor]/[package-name]. The vendor is typically the github username, though it does not have to be. Regardless of what is used for the vendor, it should be unique to the author to avoid namespace collisions. For example, my Asynchronous SQL uses the namespace JohnCurt\AsyncMySQL\. To ensure that the backslash is always a backslash, composer requires that it be escaped, so the namespace would be entered in the file with double backspaces: JohnCurt\\AsyncMySQL\\. The final result is as follows:

{
    "name": "johnfansler/library-name",
    "description": "My Test Library",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "John Fansler",
            "email": "engaged@engagedphp.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "require-dev": {
        "phpunit/phpunit": "^7.1@dev"
    },
    "autoload": {
        "psr-4": {"VendorNamespace\\LibraryNamespace\\": "src/"}
    }
}

The version of PHPUnit in your file might be different but as versions progress, the process this far should remain the same.

At this point we have everything that is needed to begin the process of Test Driven Development. PHPUnit is ready to run any tests in the ./tests directory, and our code goes in the ./src directory!

Test Driven Development

TDD is a coding style that seems very tedious at first and, indeed, in the beginning it will seem like it is holding you back seriously.  However, it is an investment worth making as it pays off many times over as the project grows bigger and bigger.

The idea is that every bit of code must be fulfilling a test, and that it isn’t even written until the corresponding test is written.  Personally, I am tempted to write a bunch of tests and then write a bunch of code to fulfill all of them. However, that is not the correct method. Basically TDD says that we should write a test that fails, then implement just enough code to solve the test and only the test. If you are new to TDD, no shortcuts should be taken, as it builds practice in the technique, but in practice, I find that there is always a little bit of untested stuff out there – even in the best code. Making high quality software implies that the tests must be high quality to ensure that the code is high quality.

So, let’s build our first test, see that it fails, and then make some code to solve it.

The main building block of Object Oriented PHP is that nearly everything is in a Class.  I would say that the first test should instantiate our class and then check that it is indeed the class we wanted. In the tests directory let’s create a file with the same name as the first Class we will be writing, but appended with Test. If making a class called MyLibrary, then the tests will be contained in MyLibraryTest.php.  Each test of the class therein, should be named using something that is readable to the developer and begins with test.  A good first test would be to create the method testInstantiationOfMyLibrary.  The class contained in the file will need to extend PHPUnit’s TestCase and will look something like this:

<?php

class MyLibraryTest extends \PHPUnit\Framework\TestCase {
	public function testInstantiationOfMyLibrary() {
		//todo...
	}
}

Inside testInstantiationOfMyLibrary we will need to first make the class we are testing, and then test that it is the right class. To help us check tests pass, we have multiple PHPUnit TestCase assertions we can use. The one in question is $this->assertInstanceOf which checks to make sure that an object is of the correct type.

<?php

use \VendorNamespace\LibraryNamespace\MyLibrary;

class MyLibraryTest extends \PHPUnit\Framework\TestCase {
	public function testInstantiationOfMyLibrary() {
		$obj = new MyLibrary();
		$this->assertInstanceOf('\VendorNamespace\LibraryNamespace\MyLibrary', $obj);
	}
}

Congratulations! We have our first test! We can run this test and make sure that it fails!

Screenshot of filing phpunit test

Indeed it fails and we can see very distinctly that it has an Error in MyLibraryTest::testInstantiationOfMyLibrary

Now we need to make our test pass! Let’s create a blank slate of a class in the ./src directory that can be loaded by the test. Following PSR-4 autoloading, the file should be named MyLibrary.php and will look like this:

<?php

namespace VendorNamespace\LibraryNamespace;

class MyLibrary {
	//todo
}

Simply making the class able to be instantiated causes our test to pass, meaning we are done coding until the next test has been written.

Screenshot of passing PHPUnit test

The next step is to continue the TDD process of creating tests and immediately writing just enough code to make them pass. More information on PHPUnit assertions (tests) can be found at https://phpunit.de/manual/current/en/appendixes.assertions.html. There are many, many assertions to choose from, each testing a slightly different aspect of the code,  I recommend getting acquainted with the list as it will also give you an idea as to what kinds of things you can be testing your code for as you go.

Once the library is complete, the last step is to submit the package to packagist. This process requires that the project be hosted on a git platform (github or bitbucket). All you do is register an account, add your project, and optionally set up github to use a webhook to tell packagist to update whenever the project is updated. It has been my experience that submission and availability in composer is practically instantaneous.

Once we are done with this, the library can be loaded into other projects very easily, using the package name entered into packagist. Congratulations! You have published your first library!

Let me know your thoughts, and ideas on this topic in the comments below! Personally, I’m new to library creation, and I’m sure there are some shortcuts that I’ve missed along the way. Do you use TDD in your company, and if so, how has it treated you? Or, what are some of your company’s reasons for not using TDD?


Spread the news

Leave a Reply

Your email address will not be published. Required fields are marked *