Tester\DomQuery is a class that extends SimpleXMLElement with methods that make it easier to test HTML or XML content.

# in $html is a string with the HTML document, in $dom we get the root element
$dom = Tester\DomQuery::fromHtml($html);

# we can test the presence of elements using CSS selectors

# or select elements as array of DomQuery
$elems = $dom->find('input[data-autocomplete]');

# or verify that the element matches the selector (from version 2.5.3)


Tester\FileMock emulates files in memory to help you to test a code which uses functions like fopen(), file_get_contents() or parse_ini_file(). For example:

# Tested class
class Logger
	public function __construct(
		private string $logFile,
	) {

	public function log(string $message): void
		file_put_contents($this->logFile, $message . "\n", FILE_APPEND);

# New empty file
$file = Tester\FileMock::create('');

$logger = new Logger($file);

# Created content testing
Assert::same("Login\nLogout\n", file_get_contents($file));


This is not an assertion, but a helper for testing private methods and property objects.

class Entity
	private $enabled;
	// ...

$ent = new Entity;

Assert::with($ent, function () {
	Assert::true($this->enabled); // accessible private $ent->enabled


The purge() method creates the specified directory and, if it already exists, deletes its entire contents. It is handy for temporary directory creation. For example in tests/bootstrap.php:

@mkdir(__DIR__ . '/tmp');  # @ - directory may already exist

define('TempDir', __DIR__ . '/tmp/' . getmypid());


Tests run in parallel. Sometimes we need not to overlap the test running. Typically database tests need to prepare database content and they need nothing disturbs them during running time of the test. In these cases we use Tester\Environment::lock($name, $dir):

Tester\Environment::lock('database', __DIR__ . '/tmp');

The first argument is a lock name. The second one is a path to directory for saving the lock. The test which acquires the lock first runs. Other tests must wait till it is completed.


Classes or methods marked as final are hard to test. Calling the Tester\Environment::bypassFinals() in a test beginning causes that keywords final are removed during the code loading.

require __DIR__ . '/bootstrap.php';


class MyClass extends NormallyFinalClass  # <-- NormallyFinalClass is not final anymore
	// ...


  • improves error dump readability (coloring included), otherwise, default PHP stack trace is printed
  • enables check that assertions have been called in test, otherwise, tests without (e.g. forgotten) assertions pass too
  • automatically starts code coverage collector when --coverage is used (described later)
  • prints the status OK or FAILURE at the end of the script


Creates the global functions test(), testException(), setUp() and tearDown() into which you can split tests.

test('test description', function () {
	Assert::same(123, foo());
	// ...


Lets you find out if the test was run directly or via the Tester.

if (getenv(Tester\Environment::VariableRunner)) {
	# run by Tester
} else {
	# another way


Tester runs tests in parallel in a given number of threads. We will find a thread number in an environmental variable when we are interested:

echo "I'm running in a thread number " . getenv(Tester\Environment::VariableThread);