Tutorial: Testing¶
Introduction¶
This tutorial explains:
Warning
It is required to run the tests from a phpBB clone of the git repository, because the test framework is not included in the download packages.
Unit Tests¶
Since unit tests are explained in more detail in the Unit Testing section of the general testing documentation and require no changes for extensions, we only add a quick example here.
Note
If you have not yet run the tests of phpBB, it is recommended to make a break here and try to run them first. This helps to make sure, you have set up everything correctly, before writing the tests for the extension.
More details are explained in the Unit Testing section.
<?php
// ext/acme/demo/tests/controller/main_test.php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace acme\demo\tests\controller;
class main_test extends \phpbb_test_case
{
/** @var \acme\demo\controller\main */
protected $controller;
protected $config;
protected $helper;
protected $language;
protected $template;
public function setUp(): void
{
parent::setUp();
$this->config = $this->getMockBuilder('\phpbb\config\config')
->disableOriginalConstructor()
->getMock();
$this->helper = $this->getMockBuilder('\phpbb\controller\helper')
->disableOriginalConstructor()
->getMock();
$this->template = $this->getMockBuilder('\phpbb\template\template')
->disableOriginalConstructor()
->getMock();
$this->language = $this->getMockBuilder('\phpbb\language\language')
->disableOriginalConstructor()
->getMock();
$this->controller = new \acme\demo\controller\main(
$this->config,
$this->helper,
$this->template,
$this->language
);
}
public function test_handle_bertie()
{
$this->helper->expects($this->once())
->method('message')
->with('NO_AUTH_SPEAKING', ['bertie'], 'NO_AUTH_OPERATION', 403)
->willReturn($this->getMock('Symfony\Component\HttpFoundation\Response'));
$this->helper->expects($this->never())
->method('render');
$response = $this->controller->handle('bertie');
$this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $response);
}
public function handle_data()
{
return [
['foo', true, 'DEMO_GOODBYE'],
['bar', false, 'DEMO_HELLO'],
];
}
/**
* @dataProvider handle_data
*/
public function test_handle($name, $config, $expected_language)
{
$language_return = $expected_language . '#' . $name;
$this->config->expects($this->once())
->method('offsetExists')
->with('acme_demo_goodbye')
->willReturn(true);
$this->config->expects($this->once())
->method('offsetGet')
->with('acme_demo_goodbye')
->willReturn($config);
$this->language->expects($this->once())
->method('lang')
->with($expected_language, $name)
->willReturn($language_return);
$this->template->expects($this->once())
->method('assign_var')
->with('DEMO_MESSAGE', $language_return);
$this->helper->expects($this->once())
->method('render')
->with('demo_body.html',test_handle_bertie() $name, 200, false)
->willReturn($this->getMock('Symfony\Component\HttpFoundation\Response'));
$response = $this->controller->handle($name);
$this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $response);
}
}
Using mocks¶
In the setUp()
method we create our controller object. However, we do not
use the actual phpBB classes which are used by the controller when opening
the page. Instead
phpunit mocks are
injected.
These mocks allow us to check how often a method is called, what the arguments are and let us specify the return value. This helps us to verify that our controller code behaves as expected. These mocks also help prevent getting false errors in cases where using the actual phpBB classes may behave unexpectedly.
Testing a method¶
In our controller we added special handling when the provided name is
bertie
. So our first test is whether the
phpbb\controller\helper::message()
method is called once with the
NO_AUTH_SPEAKING
error message. We also want to make sure that the
phpbb\controller\helper::render()
method is not called in this case.
Then we call the controller’s handle()
method. If the methods are invoked
correctly the test will pass.
public function test_handle_bertie()
{
$this->helper->expects($this->once())
->method('message')
->with('NO_AUTH_SPEAKING', ['bertie'], 'NO_AUTH_OPERATION', 403)
->willReturn($this->getMock('Symfony\Component\HttpFoundation\Response'));
$this->helper->expects($this->never())
->method('render');
$response = $this->controller->handle('bertie');
$this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $response);
}
Note
Make sure that the name of your testing method starts with test
.
Otherwise the test will not be executed by phpunit.
Data providers¶
In most cases we will want to provide some test data to our unit test methods.
A data provider is a method that returns an array of arrays containing input
variables and expected output variables. The inner array contains the arguments
for the test
method.
public function handle_data()
{
return [
['foo', true, 'DEMO_GOODBYE'],
['bar', false, 'DEMO_HELLO'],
];
}
This data provider contains two arrays of test data, so our test will be called twice, once with the arguments:
‘foo’
true
‘DEMO_GOODBYE’
and a second time with:
‘bar’
false
‘DEMO_HELLO’
In the test we then tell the phpbb\config\config
mock to expect to be
called only once, and to return the specified value (which is passed in as an
argument) for the acme_demo_goodbye
config variable.
/**
* @dataProvider handle_data
*/
public function test_handle($name, $config, $expected_language)
{
// ...
$this->config->expects($this->once())
->method('offsetGet')
->with('acme_demo_goodbye')
->willReturn($config);
If we have a short look at our controller again, we see that the value of the
config influences the \phpbb\language\language::lang()
call:
$l_message = empty($this->config['acme_demo_goodbye']) ? 'DEMO_HELLO' : 'DEMO_GOODBYE';
$this->template->assign_var('DEMO_MESSAGE', $this->language->lang($l_message, $name));
This is what we check with the third argument $expected_language
of our test
method:
$this->language->expects($this->once())
->method('lang')
->with($expected_language, $name)
->willReturn($language_return);
For more information about Mocks and phpunit, please have a look at the phpunit Documentation.
phpunit configuration file¶
Before we can run the tests we need to create the configuration file for
phpunit. Don’t be scared if you do not understand it, you should not need to
edit anything in that file.
The file should be stored as ext/acme/demo/phpunit.xml.dist
:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="true"
backupStaticAttributes="true"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
verbose="true"
bootstrap="../../../../tests/bootstrap.php"
>
<testsuites>
<testsuite name="Extension Test Suite">
<directory suffix="_test.php">./tests</directory>
<exclude>./tests/functional</exclude>
</testsuite>
<testsuite name="Extension Functional Tests">
<directory suffix="_test.php">./tests/functional/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./</directory>
<exclude>
<directory suffix=".php">./language/</directory>
<directory suffix=".php">./migrations/</directory>
<directory suffix=".php">./tests/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
Now we can finally run the test suite by executing the following command:
$ ./phpBB/vendor/bin/phpunit -c phpBB/ext/acme/demo/phpunit.xml.dist
Results:
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
Runtime: PHP 7.1.33 with Xdebug 2.7.2
Configuration: /home/user/phpBB/phpBB/ext/acme/demo/phpunit.xml.dist
... 3 / 3 (100%)
Time: 101 ms, Memory: 9.00Mb
OK (3 tests, 12 assertions)
To run only the tests from one file just append the relative path to the call:
$ ./phpBB/vendor/bin/phpunit -c phpBB/ext/acme/demo/phpunit.xml.dist phpBB/ext/acme/demo/tests/controller/main_test.php
Results:
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
Runtime: PHP 7.1.33 with Xdebug 2.7.2
Configuration: /home/user/phpBB/phpBB/ext/acme/demo/phpunit.xml.dist
... 3 / 3 (100%)
Time: 92 ms, Memory: 9.00Mb
OK (3 tests, 12 assertions)
Unit tests with database interaction¶
When testing your own tables and columns, you can not use the normal database that phpBB’s unit tests set up by default.
Migration with database changes¶
Our extension has a migration file
ext/acme/demo/migrations/add_database_changes.php
, which contains some
database changes, so we can test them:
<?php
// ext/acme/demo/migrations/add_database_changes.php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace acme\demo\migrations;
use phpbb\db\migration\migration;
class add_database_changes extends migration
{
public function effectively_installed()
{
return $this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_acme');
}
static public function depends_on()
{
return ['\acme\demo\migrations\add_module'];
}
public function update_schema()
{
return [
'add_tables' => [
$this->table_prefix . 'acme_demo' => [
'COLUMNS' => [
'acme_id' => ['UINT', null, 'auto_increment'],
'acme_name' => ['VCHAR:255', ''],
],
'PRIMARY_KEY' => 'acme_id',
],
],
'add_columns' => [
$this->table_prefix . 'users' => [
'user_acme' => ['UINT', 0],
],
],
];
}
public function revert_schema()
{
return [
'drop_columns' => [
$this->table_prefix . 'users' => [
'user_acme',
],
],
'drop_tables' => [
$this->table_prefix . 'acme_demo',
],
];
}
}
Testing database changes¶
If we add a new test that checks for the existence of the table, we will see that the test fails:
<?php
// ext/acme/demo/tests/migrations/database/add_database_changes.php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace acme\demo\tests\migrations;
class add_database_changes_test extends \phpbb_database_test_case
{
/** @var \phpbb\db\tools */
protected $db_tools;
/** @var string */
protected $table_prefix;
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/add_database_changes.xml');
}
public function setUp(): void
{
parent::setUp();
global $table_prefix;
$this->table_prefix = $table_prefix;
$db = $this->new_dbal();
$this->db_tools = new \phpbb\db\tools($db);
}
public function test_user_acme_column()
{
$this->assertTrue($this->db_tools->sql_column_exists(USERS_TABLE, 'user_acme'), 'Asserting that column "user_acme" exists');
}
public function test_acme_demo_table()
{
$this->assertTrue($this->db_tools->sql_table_exists($this->table_prefix . 'acme_demo'), 'Asserting that column "' . $this->table_prefix . 'acme_demo" does not exist');
}
}
Before we can run the database test, we need to create the fixture file we
specified in the getDataSet()
method. This file can be used to create
database entries before each test run is executed. We will make use of this
later. For now we just want to check if our tables have been created, so we
specify an empty config table
ext/acme/demo/tests/migrations/database/fixtures/add_database_changes.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<table name="phpbb_config">
<column>config_name</column>
<column>config_value</column>
<column>is_dynamic</column>
</table>
</dataset>
Execution¶
When we now execute the tests again they will fail:
$ ./phpBB/vendor/bin/phpunit -c phpBB/ext/acme/demo/phpunit.xml.dist
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
Runtime: PHP 7.1.33 with Xdebug 2.7.2
Configuration: /home/user/phpBB/phpBB/ext/acme/demo/phpunit.xml.dist
...FF 5 / 5 (100%)
Time: 5.27 seconds, Memory: 10.75Mb
There were 2 failures:
1) acme\demo\tests\migrations\add_database_changes_test::test_user_acme_column
Asserting that column "user_acme" exists
Failed asserting that false is true.
/home/user/phpBB/phpBB/ext/acme/demo/tests/migrations/add_database_changes_test.php:42
2) acme\demo\tests\migrations\add_database_changes_test::test_acme_demo_table
Asserting that column "phpbb_acme_demo" does not exist
Failed asserting that false is true.
/home/user/phpBB/phpBB/ext/acme/demo/tests/migrations/add_database_changes_test.php:47
FAILURES!
Tests: 5, Assertions: 14, Failures: 2.
In order to get our database changes executed in unit tests, we need to tell
phpBB that this test needs the extension to be set up. We can do this by
overwriting the \phpbb_database_test_case::setup_extensions()
method of the
test and returning an array with the extension name:
class add_database_changes_test extends \phpbb_database_test_case
{
static protected function setup_extensions()
{
return ['acme/demo'];
}
...
and now the test passes successfully:
$ ./phpBB/vendor/bin/phpunit -c phpBB/ext/acme/demo/phpunit.xml.dist
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
Runtime: PHP 7.1.33 with Xdebug 2.7.2
Configuration: /home/user/phpBB/phpBB/ext/acme/demo/phpunit.xml.dist
..... 5 / 5 (100%)
Time: 5.45 seconds, Memory: 13.75Mb
OK (5 tests, 14 assertions)
Note
As you can see, the time for the test execution went up from a few hundred milliseconds to a few seconds. This is because database tests set up the database and populate it, which just takes time.
Therefore it is recommended to only use database tests when you really need the database. It is better to split your test file into a database-test and a non-database one, to keep the run time short.
Using fixtures¶
Now let’s make use of the fixture file to populate the database for our test.
Therefore we replace the content of the
ext/acme/demo/tests/migrations/database/fixtures/add_database_changes.xml
file with the following content:
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<table name="phpbb_acme_demo">
<column>acme_id</column>
<column>acme_name</column>
<row>
<value>1</value>
<value>one</value>
</row>
<row>
<value>2</value>
<value>two</value>
</row>
</table>
</dataset>
Note
You can leave out columns from the tables, if you do not want to specify values for them.
Warning
Database columns that are specified as TEXT_UNI
columns need to be
specified, otherwise your test will fail on some databases.
After we added the content to the database we add a new test at the end of the
file, that queries our table to the
ext/acme/demo/tests/migrations/database/add_database_changes.php
and tests
whether the values are really in the database:
...
public function data_acme_demo_content()
{
return [
[1, 'one'],
[2, 'two'],
];
}
/**
* @dataProvider data_acme_demo_content
*
* @param int $acme_id
* @param string $expected
*/
public function test_acme_demo_content($acme_id, $expected)
{
/** @var \phpbb\db\driver\driver_interface $db */
$db = $this->new_dbal();
$sql = 'SELECT acme_name
FROM ' . $this->table_prefix . 'acme_demo
WHERE acme_id = ' . (int) $acme_id;
$result = $db->sql_query($sql);
$value = $db->sql_fetchfield('acme_name');
$db->sql_freeresult($result);
$this->assertEquals($expected, $value);
}
}
Functional testing¶
Functional tests simulate calling a URL and allow you to filter the output then and check whether certain elements have a specific content.
Note
Again it is recommended to run the functional tests of phpBB first, before writing the tests for the extension.
More details are explained in the Functional Testing section.
Again like with the database changes we need to tell phpBB that the test depends on the extension, then phpBB will take care of enabling the extension before the test execution. Our little test opens the route we added and then checks for the right message, like the unit test we wrote in unit tests at the beginning:
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace acme\demo\tests\functional;
/**
* @group functional
*/
class demo_test extends \phpbb_functional_test_case
{
static protected function setup_extensions()
{
return ['acme/demo'];
}
public function test_demo_world()
{
$this->add_lang_ext('acme/demo', 'demo');
$crawler = self::request('GET', 'app.php/demo/world');
$this->assertStringContainsString($this->lang('DEMO_HELLO', 'world'), $crawler->filter('h2')->text());
}
public function test_demo_bertie()
{
$this->add_lang_ext('acme/demo', 'demo');
$crawler = self::request('GET', 'app.php/demo/bertie');
$this->assertStringContainsString($this->lang('NO_AUTH_SPEAKING', 'bertie'), $crawler->filter('#message p')->text());
}
}
Running this test, however, will fail:
$ ./phpBB/vendor/bin/phpunit -c phpBB/ext/acme/demo/phpunit.xml.dist
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
Runtime: PHP 7.1.33 with Xdebug 2.7.2
Configuration: /home/user/phpBB/phpBB/ext/acme/demo/phpunit.xml.dist
........F 9 / 9 (100%)
Time: 22.37 seconds, Memory: 17.25Mb
There was 1 failure:
1) acme\demo\tests\functional\demo_test::test_demo_bertie
HTTP status code does not match
Failed asserting that 403 matches expected 200.
/home/user/phpBB/tests/test_framework/phpbb_functional_test_case.php:900
/home/user/phpBB/tests/test_framework/phpbb_functional_test_case.php:859
/home/user/phpBB/tests/test_framework/phpbb_functional_test_case.php:138
/home/user/phpBB/phpBB/ext/acme/demo/tests/functional/demo_test.php:38
FAILURES!
Tests: 9, Assertions: 49, Failures: 1.
The reason is that the test suite compares the response for the correct format
(valid HTML, without debug errors) and a successful status code 200
.
Therefore we need to adjust the bertie test, because we return a 403
status
in the controller, if someone tries to talk to bertie:
public function test_demo_bertie()
{
$this->add_lang_ext('acme/demo', 'demo');
$crawler = self::request('GET', 'app.php/demo/bertie', [], false);
self::assert_response_html(403);
$this->assertStringContainsString($this->lang('NO_AUTH_SPEAKING', 'bertie'), $crawler->filter('#message p')->text());
}
Now the tests will pass correctly:
$ ./phpBB/vendor/bin/phpunit -c phpBB/ext/acme/demo/phpunit.xml.dist
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
Runtime: PHP 7.1.33 with Xdebug 2.7.2
Configuration: /home/user/phpBB/phpBB/ext/acme/demo/phpunit.xml.dist
......... 9 / 9 (100%)
Time: 22.11 seconds, Memory: 17.00Mb
OK (9 tests, 52 assertions)
Note
Functional tests are slow. Depending on your server, it might take up to 2 seconds per page view. phpBB is installed via page views as well, which takes another 20 to 100 seconds, depending on various configurations, for the first functional tests. Subsequent functional tests do not reinstall the board, so they do not have the long setup time.
Continuous integration with Github Actions¶
As a final step in this tutorial, we want to explain how to set up automated testing of your extension using Github Actions. In order to do that, your extension must first be set up as a project repository on GitHub (free of charge for public open source repositories).
If you need help setting up git and creating your GitHub project, please have a look at the Help section on Github, particularly the following two help topics:
Note
It is recommended to use the root of the extension (ext/acme/demo
) as
the root for the Git repository. Otherwise the scripts that phpBB provides for
easy test execution on Travis CI will not work correctly.
View one of phpBB’s official extension repositories as an example: Board Rules.
Create your Github Action Workflow file¶
From your repository on GitHub.com, click Add File and select Create new file
and name the file .github/workflows/tests.yml
.
Copy the following into the tests.yml
file:
name: Tests
env:
EXTNAME: acme/demo # Your extension vendor/package name
SNIFF: 1 # Run code sniffer on your code? 1 or 0
IMAGE_ICC: 1 # Run icc profile sniffer on your images? 1 or 0
EPV: 1 # Run EPV (Extension Pre Validator) on your code? 1 or 0
EXECUTABLE_FILES: 1 # Run check for executable files? 1 or 0
PHPBB_BRANCH: 3.3.x # The phpBB branch to run tests on
on:
push:
branches: # Run tests when commits are pushed to these branches in your repo
- master
- develop
pull_request: # Run tests when pull requests are made on these branches in your repo
branches:
- master
- develop
jobs:
# START Basic Checks Job (EPV, code sniffer, images check, etc.)
basic-checks:
runs-on: ubuntu-20.04
strategy:
matrix:
include:
- php: '7.2'
db: "none"
NOTESTS: 1
name: PHP ${{ matrix.php }} - ${{ matrix.db }}
steps:
- name: Checkout phpBB
uses: actions/checkout@v3
with:
repository: phpbb/phpbb
ref: ${{ env.PHPBB_BRANCH }}
path: phpBB3
- name: Checkout extension
uses: actions/checkout@v3
with:
path: phpBB3/phpBB/ext/${{ env.EXTNAME }}
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysqli, sqlite, pdo_sqlite, intl, gd, exif, iconv, sqlsrv, pdo_sqlsrv, ldap
coverage: none
- name: Setup environment for phpBB
env:
DB: ${{ matrix.db }}
PHP_VERSION: ${{ matrix.php }}
NOTESTS: '1'
run: .github/setup-phpbb.sh $DB $PHP_VERSION $NOTESTS
working-directory: ./phpBB3
- name: Setup EPV
if: ${{ env.EPV != 0 }}
run: composer require phpbb/epv:dev-master --dev --no-interaction --ignore-platform-reqs
working-directory: ./phpBB3/phpBB
- name: Run code sniffer
if: ${{ env.SNIFF != 0 }}
env:
NOTESTS: '1'
run: .github/ext-sniff.sh $EXTNAME $NOTESTS
working-directory: ./phpBB3
- name: Check image ICC profiles
if: ${{ env.IMAGE_ICC != 0 }}
run: .github/check-image-icc-profiles.sh
working-directory: ./phpBB3
- name: Check executable files
if: ${{ env.EXECUTABLE_FILES != 0 }}
run: .github/ext-check-executable-files.sh ./ $EXTNAME
working-directory: ./phpBB3
- name: Run EPV
if: ${{ env.EPV != 0 }}
run: phpBB/vendor/bin/EPV.php run --dir="phpBB/ext/$EXTNAME/"
working-directory: ./phpBB3
# END Basic Checks Job
# START MySQL and MariaDB Job
mysql-tests:
runs-on: ubuntu-20.04
strategy:
matrix:
include:
- php: '7.2'
db: "mariadb:10.1"
- php: '7.2'
db: "mariadb:10.2"
- php: '7.2'
db: "mariadb:10.3"
- php: '7.2'
db: "mariadb:10.4"
- php: '7.2'
db: "mariadb:10.5"
- php: '7.2'
db: "mysql:5.6"
db_alias: "MyISAM Tests"
MYISAM: 1
- php: '7.2'
db: "mysql:5.6"
- php: '7.2'
db: "mysql:5.7"
- php: '7.3'
db: "mysql:5.7"
- php: '7.4'
db: "mysql:5.7"
- php: '7.4'
db: "mysql:8.0"
- php: '8.0'
db: "mysql:5.7"
- php: '8.1'
db: "mysql:5.7"
- php: '8.2'
db: "mysql:5.7"
name: PHP ${{ matrix.php }} - ${{ matrix.db_alias != '' && matrix.db_alias || matrix.db }}
services:
mysql:
image: ${{ matrix.db }}
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: phpbb_tests
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- name: Checkout phpBB
uses: actions/checkout@v3
with:
repository: phpbb/phpbb
ref: ${{ env.PHPBB_BRANCH }}
path: phpBB3
- name: Checkout extension
uses: actions/checkout@v3
with:
path: phpBB3/phpBB/ext/${{ env.EXTNAME }}
- id: database-type
env:
MATRIX_DB: ${{ matrix.db }}
run: |
db=$(echo "${MATRIX_DB%%:*}")
echo "db=$db" >> $GITHUB_OUTPUT
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysqli, sqlite, pdo_sqlite, intl, gd, exif, iconv, sqlsrv, pdo_sqlsrv, ldap
coverage: none
- name: Setup environment for phpBB
env:
DB: ${{steps.database-type.outputs.db}}
PHP_VERSION: ${{ matrix.php }}
NOTESTS: '0'
run: .github/setup-phpbb.sh $DB $PHP_VERSION ${NOTESTS:-0}
working-directory: ./phpBB3
- name: Setup database
env:
DB: ${{steps.database-type.outputs.db}}
MYISAM: ${{ matrix.MYISAM != 1 && '0' || '1' }}
run: .github/setup-database.sh $DB $MYISAM
working-directory: ./phpBB3
- name: Setup PHPUnit files
run: mkdir -p phpBB/ext/$EXTNAME/.github && cp .github/phpunit* $_
working-directory: ./phpBB3
- name: Run unit tests
env:
DB: ${{steps.database-type.outputs.db}}
run: phpBB/vendor/bin/phpunit --configuration phpBB/ext/$EXTNAME/.github/phpunit-$DB-github.xml --bootstrap ./tests/bootstrap.php
working-directory: ./phpBB3
# END MySQL and MariaDB Job
# START PostgreSQL Job
postgres-tests:
runs-on: ubuntu-20.04
strategy:
matrix:
include:
- php: '7.2'
db: "postgres:9.5"
- php: '7.2'
db: "postgres:9.6"
- php: '7.2'
db: "postgres:10"
- php: '7.2'
db: "postgres:11"
- php: '7.2'
db: "postgres:12"
- php: '7.2'
db: "postgres:13"
- php: '7.3'
db: "postgres:13"
- php: '7.4'
db: "postgres:13"
- php: '8.0'
db: "postgres:12"
- php: '8.0'
db: "postgres:13"
- php: '8.1'
db: "postgres:14"
- php: '8.2'
db: "postgres:14"
name: PHP ${{ matrix.php }} - ${{ matrix.db }}
services:
postgres:
image: ${{ matrix.db != 'postgres:9.5' && matrix.db != 'postgres:9.6' && matrix.db != 'postgres:10' && matrix.db != 'postgres:11' && matrix.db != 'postgres:12' && matrix.db != 'postgres:13' && 'postgres:10' || matrix.db }}
env:
POSTGRES_HOST: localhost
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
-v /var/run/postgresql:/var/run/postgresql
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- name: Checkout phpBB
uses: actions/checkout@v3
with:
repository: phpbb/phpbb
ref: ${{ env.PHPBB_BRANCH }}
path: phpBB3
- name: Checkout extension
uses: actions/checkout@v3
with:
path: phpBB3/phpBB/ext/${{ env.EXTNAME }}
- id: database-type
env:
MATRIX_DB: ${{ matrix.db }}
run: |
db=$(echo "${MATRIX_DB%%:*}")
echo "db=$db" >> $GITHUB_OUTPUT
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysqli, sqlite, pdo_sqlite, intl, gd, exif, iconv, sqlsrv, pdo_sqlsrv, ldap
coverage: none
- name: Setup environment for phpBB
env:
DB: ${{steps.database-type.outputs.db}}
PHP_VERSION: ${{ matrix.php }}
NOTESTS: '0'
run: .github/setup-phpbb.sh $DB $PHP_VERSION ${NOTESTS:-0}
working-directory: ./phpBB3
- name: Setup database
env:
DB: ${{steps.database-type.outputs.db}}
MYISAM: '0'
run: .github/setup-database.sh $DB $MYISAM
working-directory: ./phpBB3
- name: Setup PHPUnit files
run: mkdir -p phpBB/ext/$EXTNAME/.github && cp .github/phpunit* $_
working-directory: ./phpBB3
- name: Run unit tests
env:
DB: ${{steps.database-type.outputs.db}}
run: phpBB/vendor/bin/phpunit --configuration phpBB/ext/$EXTNAME/.github/phpunit-$DB-github.xml --bootstrap ./tests/bootstrap.php
working-directory: ./phpBB3
# END PostgreSQL Job
# START Other Tests Job (SQLite 3 and mssql)
other-tests:
runs-on: ubuntu-20.04
strategy:
matrix:
include:
- php: '7.2'
db: "sqlite3"
- php: '7.2'
db: "mcr.microsoft.com/mssql/server:2017-latest"
db_alias: 'MSSQL 2017'
- php: '7.2'
db: "mcr.microsoft.com/mssql/server:2019-latest"
db_alias: 'MSSQL 2019'
name: PHP ${{ matrix.php }} - ${{ matrix.db_alias != '' && matrix.db_alias || matrix.db }}
services:
mssql:
image: ${{ matrix.db != 'mcr.microsoft.com/mssql/server:2017-latest' && matrix.db != 'mcr.microsoft.com/mssql/server:2019-latest' && 'mcr.microsoft.com/mssql/server:2017-latest' || matrix.db }}
env:
SA_PASSWORD: "Pssw0rd_12"
ACCEPT_EULA: "y"
ports:
- 1433:1433
options: >-
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'Pssw0rd_12' -Q \"Use [master]; CREATE DATABASE [phpbb_tests] COLLATE Latin1_General_CI_AS\" || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 5
--health-start-period 10s
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- name: Checkout phpBB
uses: actions/checkout@v3
with:
repository: phpbb/phpbb
ref: ${{ env.PHPBB_BRANCH }}
path: phpBB3
- name: Checkout extension
uses: actions/checkout@v3
with:
path: phpBB3/phpBB/ext/${{ env.EXTNAME }}
- id: database-type
env:
MATRIX_DB: ${{ matrix.db }}
run: |
if [ $MATRIX_DB == 'mcr.microsoft.com/mssql/server:2017-latest' ] || [ $MATRIX_DB == 'mcr.microsoft.com/mssql/server:2019-latest' ]
then
db='mssql'
else
db=$(echo "${MATRIX_DB%%:*}")
fi
echo "db=$db" >> $GITHUB_OUTPUT
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysqli, sqlite, pdo_sqlite, intl, gd, exif, iconv, sqlsrv, pdo_sqlsrv, ldap
coverage: none
- name: Setup environment for phpBB
env:
DB: ${{steps.database-type.outputs.db}}
PHP_VERSION: ${{ matrix.php }}
NOTESTS: '0'
run: .github/setup-phpbb.sh $DB $PHP_VERSION ${NOTESTS:-0}
working-directory: ./phpBB3
- name: Setup database
env:
DB: ${{steps.database-type.outputs.db}}
MYISAM: '0'
run: .github/setup-database.sh $DB $MYISAM
working-directory: ./phpBB3
- name: Setup PHPUnit files
run: mkdir -p phpBB/ext/$EXTNAME/.github && cp .github/phpunit* $_
working-directory: ./phpBB3
- name: Run unit tests
env:
DB: ${{steps.database-type.outputs.db}}
run: phpBB/vendor/bin/phpunit --configuration phpBB/ext/$EXTNAME/.github/phpunit-$DB-github.xml --bootstrap ./tests/bootstrap.php
working-directory: ./phpBB3
# END Other Tests Job
Note
You must change the EXTNAME
variable to your extension’s name (as you defined it in
your composer.json file) in the env
section at the top of this file:
env:
EXTNAME: acme/demo # Your extension vendor/package name
SNIFF: 1 # Run code sniffer on your code? 1 or 0
IMAGE_ICC: 1 # Run icc profile sniffer on your images? 1 or 0
EPV: 1 # Run EPV (Extension Pre Validator) on your code? 1 or 0
EXECUTABLE_FILES: 1 # Run check for executable files? 1 or 0
PHPBB_BRANCH: 3.3.x # The phpBB branch to run tests on
To save and run your workflow, scroll to the bottom of the page and select Create a new branch for this commit and start a pull request. Then, to create a pull request, click Propose new file. After you merge this pull request, all future commits and pull requests on your master branch will trigger this CI workflow and your unit, database and functional tests will be executed.
Customising Your Test Workflow¶
You’ll notice the env
section of the workflow has options where you can enable or
disable basic tests such as the EPV, Code Sniffer, checking any images in your extension
for ICC profiles and checking for executable text files by changing their variables to
either 1 or 0.
You can also set the version of phpBB you want your extension tested in using the
PHPBB_BRANCH
variable. This tutorial is using the current version of phpBB which
is the 3.3.x branch. Older branches of phpBB are not being supported with Github Actions,
but can instead be used with Travis-CI.com .
Finally you may also make changes to the jobs. This tutorial’s workflow is split up into several jobs based on the databases being tested:
Basic Checks: The
basic-checks
job does not do any PHPUnit testing. This is where EPV, Code Sniffer and Image Profile checks run. If you never intend to run these checks you may either delete or comment out the entirebasic-checks
job.MySQL Tests: The
mysql-tests
job runs PHPUnit tests in various MySQL and MariaDB and PHP combinations. They are all defined in thematrix
section. This workflow only runs in versions of PHP 7 and PHP 8 but you could, for example, add tests for versions of PHP 5. (Note that to include PHP 5 in the test matrix you must change the ubuntu server for that job toruns-on: ubuntu-16.04
). You may delete or comment out some of the tests in the matrix if you do not want to test certain versions of PHP or MySQL or MariaDB.PostgreSQL Tests: The
postgres-tests
job runs PHPUnit tests in various versions of PostgreSQL. The PHP version is consistent throughout since the mysql-checks job is where we do most of our PHP environment checks. You may add additional checks to this matrix or you may either delete or comment out the entirepostgres-tests
job if you do not intend to test PostgreSQL.MSSQL, SQLite: The
other-tests
job runs PHPUnit tests in various versions of MSSQL and SQLite3. You may either delete or comment out the MSSQL or SQLite3 checks in the matrix if you do not want to test on either of those databases, or delete or comment out the entireother-tests
job if you do not intend to test any of these databases.
Final Thoughts on Extension Testing¶
Well written tests help prevent regressions (breaking other parts of your code) by alerting you to any problems resulting from changes to your code while fixing bugs, adding new features and other code changes to your extension.
If your tests fail after committing changes, you will receive a notification email from GitHub. The logs from your Github Actions can be a little daunting at first, but once you get used to reading them they can help you pinpoint unforeseen bugs and regressions in your code that must be fixed.
Github Actions also provides Build Status badges. They provide you the code in markdown
format so you can add the badge to your repository’s README so visitors can see
the build status of your extension. For example (Just change GITHUB-USERNAME/REPO-NAME
to your repository):