Testing
It is essential to have proper test coverage for the package's provided code. Adding tests to our package can confirm the existing code's behavior, verify everything still works whenever adding new functionality, and ensure we can safely refactor our package with confidence at a later stage.
Additionally, having good code coverage can motivate potential contributors by giving them more confidence that their addition does not break something else in the package. Tests also allow other developers to understand how specific features of your package are to be used and give them confidence about your package's reliability.
Installing PHPUnit
There are many options to test behavior in PHP, Laravel is built with testing in mind and provides support for testing with Pest and PHPUnit. We can install PHPUnit, which is the default testing framework for Laravel.
Install PHPUnit as a dev-dependency in our package:
Installing Pest
Alternatively, we can use Pest as our testing framework. It's important to note that Pest is built on top of PHPUnit, which means that all the options offered by PHPUnit can also be used in Pest. Therefore, our configurations for PHPUnit will also apply to Pest tests.
Install Pest as a dev-dependency in our package and initialize it:
The --init
flag will create a tests
directory with example tests, a TestCase.php
file and a Pest.php
file. It will also create a phpunit.xml
file with the necessary configurations for Pest, this saves us from having to create these files manually.
In the Pest.php
file, we can uncomment the uses
method and add the TestCase
class to the uses
method. This will allow us to use the TestCase
class in our tests.
<?php
...
uses(JohnDoe\BlogPackage\Tests\TestCase::class)->in('Feature');
uses(JohnDoe\BlogPackage\Tests\TestCase::class)->in('Unit');
...
Note: you might need to install a specific version if you're developing a package for an older version of Laravel. Also to install orchestra/testbench
, please refer to Orchestra Testbench set up on the Development Environment page.
To configure PHPUnit, create a phpunit.xml
file in the root directory of the package.
Then, copy the following template to use an in-memory sqlite database and enable colorful reporting.
phpunit.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="vendor/autoload.php"
backupGlobals="false"
colors="true"
processIsolation="false"
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false"
>
<coverage/>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<php>
<env name="DB_CONNECTION" value="testing"/>
<env name="APP_KEY" value="base64:2fl+Ktvkfl+Fuz4Qp/A75G2RTiWVA/ZoKZvp6fiiM10="/>
</php>
<source>
<include>
<directory suffix=".php">src/</directory>
</include>
</source>
</phpunit>
Note the dummy APP_KEY
in the example above. This environment variable is consumed by Laravel's encrypter, which your tests might be making use of. For most cases, the dummy value will be sufficient. However, you are free to either change this value to reflect an actual app key (of your Laravel application) or leave it off entirely if your test suite does not interact with the encrypter.
Directory Structure
To accommodate Feature and Unit tests, create a tests/
directory with a Unit
and Feature
subdirectory and a base TestCase.php
file. The structure looks as follows:
The TestCase.php
extends \Orchestra\Testbench\TestCase
(see example below) and contains tasks related to setting up our “world” before each test is executed. In the TestCase
class we will implement three important set-up methods: setUp()
, getEnvironmentSetUp()
and getPackageProviders()
.
Let's look at these methods one by one:
-
setUp()
: You might have already used this method in your tests. Often it is used when you need a certain model in all following tests. The instantiation of that model can therefore be extracted to asetUp()
method which is called before each test. Within the tests, the desired model can be retrieved from the Test class instance variable. When using this method, don't forget to call the parentsetUp()
method (and make sure to return void). -
getEnvironmentSetUp()
: As suggested by Orchestra Testbench: "If you need to add something early in the application bootstrapping process, you could use thegetEnvironmentSetUp()
method". Therefore, I suggest it is called before thesetUp()
method(s). -
getPackageProviders()
: As the name suggests, we can load our service provider(s) within thegetPackageProviders()
method. We'll do that by returning an array containing all providers. For now, we'll just include the package specific package provider, but imagine that if the package uses anEventServiceProvider
, we would also register it here.
In a package, TestCase
will inherit from the Orchestra Testbench TestCase:
// 'tests/TestCase.php'
<?php
namespace JohnDoe\BlogPackage\Tests;
use JohnDoe\BlogPackage\BlogPackageServiceProvider;
class TestCase extends \Orchestra\Testbench\TestCase
{
public function setUp(): void
{
parent::setUp();
// additional setup
}
protected function getPackageProviders($app)
{
return [
BlogPackageServiceProvider::class,
];
}
protected function getEnvironmentSetUp($app)
{
// perform environment setup
}
}
Before we can run the PHPUnit test suite, we first need to map our testing namespace to the appropriate folder in the composer.json file under an "autoload-dev" (psr-4) key:
Finally, re-render the autoload file by running composer dump-autoload
.
Authentication
In some cases you might want to use Laravel's User::class
to be able to use an authenticated user in your tests.
There are several approaches, as discussed in the Models related to App\User section. However, if you don't have any relationships with the User
model, and only want to test authentication logic, the easiest option is to create your own User
class, extending the Illuminate\Foundation\Auth\User
class:
<?php
use Illuminate\Foundation\Auth\User as BaseUser;
class User extends BaseUser
{
protected $table = 'users';
}
After defining this custom User
model within your package, you should execute the migrate command from the Orchestra
package to create the users
table in your test database:
<?php
$this->loadLaravelMigrations(['--database' => 'testbench']);
$this->artisan('migrate', ['--database' => 'testbench'])->run();
Finally, you can use the package's User::class
in your tests within the $this->actingAs()
helper and send a request by an authenticated user.