Automated Tests

To ensure code integrity as new features are added and existing features are altered by patches, phpBB utilizes the PHPUnit test framework. This way, the development team can keep track of whether or not a change made to the core will effect other existing features in unforeseen ways.

There are two main types of tests that can be written and run: unit tests, and functional tests.

Unit Tests
Unit tests simply verify that specific portions of the source code work properly. These tests are located in the main phpBB repository at ./tests/ and are grouped into folders based on commonality between the functionality they are testing. For instance, tests that are meant to test the config will be in ./tests/config/.

Running Unit Tests
Information on how to run tests is available in the GitHub repository at Running Tests. You can switch the branch to check instructions for a specific version of phpBB.

Writing Unit Tests
Files that contain tests should have a `_test` suffix, e.g.: tests/demo/demo_test.php

tests/demo/demo_test.php
Within unit tests, there are two categories: tests that need a database, and those that do not. If a test does not need a database then don't use it!

Without a Database
<?php /**
 * @package testing
 * @copyright (c) 2008 phpBB Group
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License

require_once dirname(__FILE__).'/../phpBB/includes/functions.php';

class phpbb_demo_demo_test extends phpbb_test_case {	public static function phpbb_email_hash_data {		return array(			array('wiki@phpbb.com', 126830126620),			array('', 0),		); }

/**	* @dataProvider phpbb_email_hash_data */	public function test_phpbb_email_hash($email, $expected) {		$this->assertEquals($expected, phpbb_email_hash($email)); } } You may have noted, that in the test-function there is actually no data. There's a dataProvider-function stated in the comment, which is used for the test. The test is then run with every array in the dataProvider array, having the values as parameters for the test-function-call.

Using a Database
createXMLDataSet(dirname(__FILE__).'/fixtures/three_users.xml'); }

public static function fetchrow_data {		return array(			array('', array(array('username_clean' => 'barfoo'),				array('username_clean' => 'foobar'),				array('username_clean' => 'bertie'))),			array('user_id = 2', array(array('username_clean' => 'foobar'))),			array("username_clean = 'bertie'", array(array('username_clean' => 'bertie'))),			array("username_clean = 'phpBB'", array),		); }

/**	* @dataProvider fetchrow_data */	public function test_fetchrow($where, $expected) {		// The function from phpbb_test_case_helpers returns a new db for every test. $db = $this->new_dbal;

$result = $db->sql_query('SELECT username_clean			FROM phpbb_users			' . (($where) ? ' WHERE '. $where : '') . '			ORDER BY user_id ASC');

$ary = array; while ($row = $db->sql_fetchrow($result)) {			$ary[] = $row; }		$db->sql_freeresult($result);

$this->assertEquals($expected, $ary); } } Most important to know for db-tests is:
 * 1) All data from the database is truncated first.
 * 2) The data from the getDataSet function is loaded into the database.  No data from any other test is available!
 * 3) If you use a table that has a column which has no default value specified (such as text columns), be sure to specify them. (See PHPBB3-10667)

Code of a DB-DataSet


Using permissions ($auth)
When you need to use the auth class, you should use a mock object for it. here is a short example of how to use it. Please note, that you need to specify the result for every function and all values you use. Also when the forum_id is not casted to int, you need to specify a second row for each permission. (Marked with // Called without int cast below) For more information how these test doubles work, see: Test Doubles getMock('auth'); $acl_get_map = array(			array('f_read', 23, true),			array('f_read', '23', true),// Called without int cast			array('m_', 23, true),			array('m_', '23', true),// Called without int cast		);

$auth->expects($this->any) ->method('acl_get') ->with($this->stringContains('_'),				$this->anything) ->will($this->returnValueMap($acl_get_map)); $this->assertTrue($auth->acl_get('f_read', 23)); $this->assertTrue($auth->acl_get('f_read', '23')); $this->assertFalse($auth->acl_get('f_read', 12)); $this->assertFalse($auth->acl_get('f_read', '12')); } }

Functional Tests
Functional tests test software the way a user would. They simulate a user browsing the website, but they do these steps in an automated way.

phpBB allows you to write such tests. This document will tell you how.

Running Functional Tests
Information on how to run tests is available in the GitHub repository at Running Tests. You can switch the branch to check instructions for a specific version of phpBB.

Writing Functional Tests
Your test case will have to inherit from the phpbb_functional_test_case class. You will be able to make requests to a fresh phpBB installation. Also be sure, to put the file into the "tests/functional/" folder and to add the  doc block above the class, so the tests are correctly executed.

<?php

/** class phpbb_functional_my_test extends phpbb_functional_test_case {   public function bootstrap {       // setup code }
 * @group functional

public function test_index {       // test code } }

There is a bootstrap function where you can put code that is run before the test. Here you can set up some initial state required for your test.

Then you can define your tests, just as you would normally, by prefixing methods with "test_". In this case we will be testing the index.

Goutte and Assertions
The functional testing framework is built using Goutte, which makes HTTP requests and allows us to interact with the page. The Goutte "client" is available in your tests through. Alternatively you can also access it directly through.

When calling request, you will get a crawler, which allows you to fetch parts of the page using CSS selectors. Here is an example:

class phpbb_my_test extends phpbb_functional_test_case {	public function test_index {		$crawler = $this->request('GET', 'index.php'); $this->assertGreaterThan(0, $crawler->filter('.topiclist')->count); } }

We perform a GET request to "index.php", then apply a CSS filter, querying for the .topiclist class. Then we make sure our query matched something.

If the board's index page were broken, our test would catch this, allowing us to detect the issue and fix it. The more things you test, the more breakages you will catch early.

For more information what you can do with Goutte, check out the GitHub project, Ryan Weaver's Goutte tutorial and Symfony's testing documentation.

Authentication
Many functional tests will require the user to be logged in. To do so, you can use the login method inherited from the phpbb_functional_test_case class. This method will assign the user's SID to the inherited class property $this->sid. You will need to append this to the URLs that the logged-in user will be navigating to in order to hold the session. For usage examples, view the logout test in tests/functional/auth_test.php.

Localization
In tests, as in phpBB itself, do not use hard-coded language unless absolutely necessary. Instead, use the $this->add_lang and $this->lang methods. Both act very similarly to their counterparts in the phpbb_user class. Note that language/en/common.php is automatically loaded by the test framework.

function test_example {   // include the file at ./phpBB/language/en/ucp.php $this->add_lang('ucp');

// we can also include multiple ones: $this->add_lang(array('memberlist', 'mcp'));

// Let's use a language key $this->assertEquals('Login', $this->lang('LOGIN'));

// And let's use a language key formatted for use with sprintf $this->assertEquals('Logout [ user ]', $this->lang('LOGOUT_USER', 'user')); } For more usage examples, please view ./tests/functional/lang_test.php.