PHPUnit Tests for your Laravel Package

5/6/2020

A very short intro on how to create PHPUnit tests for your Laravel packages.

# Install Laravel

Before we can do anything, we have to setup setup a new Laravel project. You can of course use an existing project, but I recommend to quickly create a new one just for the purpose of this tutorial.

You can create a new Laravel project using the Laravel CLI or Composer. Replace <project-name> with the name you choose for your project.

laravel new <project-name>
# or
composer create-project --prefer-dist laravel/laravel <project-name> 

# Create Composer Package

In the past I used to have a folder packages at the root of my Laravel projects in which I develop but that was always quite annoying, because I had no way to use git on the Laravel instance, since there where other git repositories in the packages folder. My current approach is to create a new folder outside of Laravel and just linking it through the composer.json file.

So let's go ahead and create a new folder somewhere outside Laravel and initialize a new Composer project. Remember to replace <folder-name>.

cd .. # go one level up from your laravel project
mkdir <folder-name> # create a new folder
cd <folder-name> # go inside the new directory
composer init # initialize a new composer project

Fill out the information that Composer asks for. When you are done, open the composer.json file and add the Laravel specific section. Remember to replace Your\\Namespace with the namespace you want to use.

{
    // ...

    "extra": {
        "laravel": {
            "providers": [
                "Your\\Namespace\\ServiceProvider"
            ]
        }
    }
}

Also we need to add the autoload section for the src/ folder. This is the section that defines all options for Composer autoloading.

{
    // ...

    "autoload": {
        "psr-4": {
            "Your\\Namespace\\": "src/"
        },
    },

    // ...
}

As you see, we are referencing the ServiceProvider class. This class is the real link between your Package and Laravel. So let's create it.

mkdir src # create source directory in your package
touch src/ServiceProvider.php # create the file

Since we are not going to add anything besides our sample test we don't need much here. Open the ServiceProvider class in any text editor and add the following. Remember to replace Your\Namespace with the namespace you are using for your package.

<?php

namespace Your\Namespace;

use Illuminate\Support\ServiceProvider;

class ServiceProvider extends ServiceProvider
{
    /**
     * Boot method of this service provider.
     *
     * @return void
     */
    public function boot()
    {
        // nothing
    }

    /**
     * Register method of this service provider.
     *
     * @return void
     */
    public function register()
    {
        // nothing
    }
}

Last thing to do for our Composer setup, is to require the package in our Laravel application. To do so open the composer.json file of your project in an editor and add the following section to it. Remember to replace <folder-name> with the actual name of the folder.

{
    // ...

    "repositories": [
        {
            "type": "path",
            "url": "../<folder-name>"
        }
    ],
}

Now we should be able to require it in our Laravel project. Open the composer.json file of the Laravel project and add the following. Remember to replace vendor/package-name with what you are actually using in your package.

{
    // ...

    "require": {
        // ...

        "vendor/package-name": "@dev"
    }

    // ...
}

Now you can just run composer update and Composer will create a symlink for your package in the vendor folder of your Laravel project. You should see an according message when composer pulls in all the dependencies.

If you missed it, you can always check if it worked by running the following. Remember to replace <your-vendor-name>/<and-package-name> with whatever you chose for during the Composer setup.

ll vendor/<your-vendor-name>/<and-package-name>

# Add a Test

Now it is time to create an actual test inside of your package. We'll use artisan to create a new test inside the Laravel project and then copy it to your package folder.

artisan make:test SampleTest # create SampleTest class
mkdir ../<folder-name>/tests # create directory for tests of your package
mv tests/Feature/SampleTest.php ../<folder-name>/tests/ # move the class to the composer package.

Now you should have the SampleTest class in the tests folder of your package. The class should look similar to the following. Make sure to update the namespaces to match the namespace you use for your package.

<?php

namespace Your\\Namespace\\Tests;

use Tests\TestCase;

class SampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

Now we need to update the composer.json file of the package so composer will also autoload the classes inside the tests folder of your package. Update the "autoload" section in your packages composer.json file as shown below.

{
    // ...

    "autoload": {
        "psr-4": {
            "Your\\Namespace\\Tests\\": "tests/",
            "Your\\Namespace\\": "src/"
        },
    },

    // ...
}

Awesome, you are almost there! just one more thing and you can execute your tests!

# Setup Testsuite in Laravel

Laravel ships with a default setup for PHPUnit. In order to execute our tests as part of this setup, we need to update the phpunit.xml file inside the root of your Laravel project as shown below. Remember to replace YourPackage and your-vendor-name/your-package-name.

<?xml version="1.0" encoding="UTF-8"?>

    <!-- ... -->

    <testsuites>

        <!-- ... -->

        <testsuite name="YourPackage">
            <directory suffix="Test.php">./vendor/your-vendor-name/your-package-name</directory>
        </testsuite>
    </testsuites>

# Done!

When you now run phpunit the tests of your package should execute along with the ones in the Laravel project. If you only want to filter for a specific test of your package (or in the Laravel project), then you can use --filter on phpunit to only execute what you need. This also works with methods!

phpunit --filter=SampleTest
# or for the method
phpunit --filter=testBasicTest

# Troubleshooting

If it happens, that composer can't find the tests of your package, then you probably have a typo in one of your composer.json files or in the namespace of your class. I'm just pointing this out because if anything goes wrong it's typically an issue with that 😉

If composer tries to load the package from packagist instead of your local package then make sure, that you don't have "preferred-install": "dist", in your composer.json file of the Laravel project It's typically there since Laravel 7. Also make sure to use @dev as version for your package when requiring in the Laravel project.

Now go out there and have fun testing your packages 😁