Unit Testing
Plugins may contain unit tests - automated tests that ensure that features and functionality within the plugin are operating correctly after any changes are made to the plugin. Winter CMS makes it easy to create and run unit tests for your plugin using the PHPUnit testing suite.
Setting up for unit testing
Plugins can be tested by creating a phpunit.xml
file in the base directory of your plugin.
plugins/
`-- acme/
`-- myplugin/
|-- Plugin.php
`-- phpunit.xml # Create this file
The file should contain the following code, at a minimum:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
>
<testsuites>
<testsuite name="Plugin Unit Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>
Creating test cases and fixtures
Unit testing, in general, involves running test cases. A single test case should test a certain aspect or functionality of the plugin. For example, if you were to create a blog plugin, you may have one test case that tests adding a blog post and then another to add a blog category and so on.
As you want the scenarios that you test to be consistent, test cases are often run with "fixtures" - single bits of static data. Using the example above for the test case involving adding a blog post, you may want to test with a certain blog title and content every time you run the tests. This means that the blog title and content are a "fixture".
We recommend the following structure for organising test cases and fixtures in your plugin, but you are free to choose your own structure.
plugins/
`-- acme/
`-- blog/
|-- tests/ # The tests folder
| |-- cases/ # The test cases folder
| | |-- BlogCategoryTest.php # An example test case
| | `-- BlogPostTest.php # Another example test case
| `-- fixtures/ # The test fixtures folder
| |-- first-blog.md # An example fixture
| `-- second-blog.md # Another example fixture
|-- Plugin.php
`-- phpunit.xml
A unit test within a plugin should extend the System\Tests\Bootstrap\TestCase
class. This base test case class includes several features for properly testing plugins, including running migrations for the plugins and ensuring that the environment is set up to properly test plugins.
In order to be picked up by PHPUnit, test cases must match the following conventions:
- Test cases must be defined within a class ended with the
Test
suffix - generally, the test case should match the path and class name being tested, however only the suffix is required. For example, if you are testing aPost
model, stored in themodels/Post.php
file in your plugin, it is best to define the test cases for this model within a class calledPostTest
and stored in thetests/cases/models/PostTest.php
path. - The test case class must extend the
System\Tests\Bootstrap\TestCase
class. - Test case methods must be public and must be prefixed with the
test
prefix in the method name. Alternatively, the method must have a docblock that defines the@test
annotation. - Test cases must make at least one assertion.
An example test case class is below:
<?php
namespace Acme\Blog\Tests\Cases\Models;
use System\Tests\Bootstrap\TestCase;
class BlogTest extends TestCase
{
// Test adding a post
public function testAddPost()
{
// ...
}
/**
* This is still a test case, as it contains the following annotation.
* @test
*/
public function itCanDeleteAPost()
{
// ...
}
// This is NOT a test case, as the method is protected.
protected function testNeverRuns()
{
// ...
}
}
Running unit tests
To run unit tests, it is strongly recommended to use the winter:test
Artisan command as opposed to PHPUnit directly, as this will ensure that the necessary bootstrapping of Winter CMS functionality takes place.
php artisan winter:test -p Your.PluginCode
This command will run all defined test cases that match the conventions above.
Automating unit tests
If you wish to use continuous integration tools to run unit tests automatically, such as GitHub Actions or GitLab CI, you will need to ensure that a Winter CMS environment is set up by your continuous integration system before running the unit tests.
In general, your automation should do the following:
- Set up an environment with the PHP version you wish to test against, as well as all required extensions.
- Download the Winter CMS version you wish to test against.
- Install all Composer dependencies.
- Run the
winter:test
Artisan command for your plugin.
Example GitHub Actions configuration
This is an example of a configuration that you can use for GitHub Actions to test a Winter plugin whenever a new commit is added to your repository. Feel free to use this and adjust as necessary for your specific requirements.
name: Tests
on:
push:
pull_request:
jobs:
unitTests:
runs-on: ubuntu-latest
name: Unit Tests
steps:
- name: Checkout Winter CMS
uses: actions/checkout@v4
with:
repository: wintercms/winter
ref: develop # change this to a different branch or tag name if you wish to test with a specific version of Winter CMS
- name: Checkout code
uses: actions/checkout@v4
with:
path: plugins/my-author/my-plugin # change this to the correct folder for your plugin
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3 # change this if you wish to test a different PHP version
extensions: mbstring, intl, gd, xml, sqlite
tools: composer:v2
- name: Install Composer dependencies
run: composer install --no-interaction --no-progress --no-scripts
- name: Run unit tests
run: php artisan winter:test -p MyAuthor.MyPlugin # change this to the correct plugin code