В процессе PHPUnit-тестирования зачастую приходится иметь дело с кодом, который зависит от встроенных функций PHP, например, phpversion()
. Методика тестирования изложена в настоящей статье.
Рассмотрим в качестве простого примера следующий класс, который проверяет текущую версию PHP и сообщает, удовлетворяет ли она требованиям.
<?php
/**
* Class to check requirements of the plugin.
*
* @package sample-plugin
*/
/**
* Class Sample_Requirements
*/
class Sample_Requirements {
/**
* Check php version.
*
* @return bool
*/
public function is_php_version_required() {
if ( version_compare( '5.6', phpversion(), '>' ) ) {
$this->php_requirement_message();
return false;
}
return true;
}
/**
* Show notice with php requirement.
*/
public function php_requirement_message() {
// Some code to show the message.
}
}
Как протестировать метод is_php_version_required()
? Можно, конечно, написать вспомогательный метод, например:
public function phpversion() {
return phpversion();
}
и обращаться к нему: $this->phpversion()
. А в тестах моделировать этот метод. Но выглядит это тяжеловесно.
Есть способ подмены встроенных функций PHP. Предоставляет его библиотека lucatume/function-mocker. Она использует библиотеку antecedent/patchwork, которая и делает основную работу через monkey patch.
В bootstrap.php
файл надо добавить use
и инициализацию FunctionMocker:
use tad\FunctionMocker\FunctionMocker;
// Main bootrstrap code...
FunctionMocker::init(
[
'whitelist' => [
realpath( PLUGIN_PATH . '/includes' ),
],
'blacklist' => [
realpath( PLUGIN_PATH ),
],
'redefinable-internals' => [ 'phpversion' ],
]
);
Здесь whitelist
— путь к папке, где расположены тестируемые классы плагина. В этой папке patchwork будет подменять функции. blacklist
— путь к корневой папке плагина, здесь замен не будет (кроме папки whitelist
). Это важно, потому что некоторые библиотеки (например, тестовая Mockery
) иначе не работают. redefinable-internals
— перечень функций, которые будут изменены при тестировании.
Теперь результат работы встроенной функции можно подменять «на лету», прямо во время выполнения теста:
<?php
/**
* Test_Sample_Requirements class file
*
* @package sample-plugin
*/
use PHPUnit\Framework\TestCase;
use tad\FunctionMocker\FunctionMocker;
/**
* Class Test_Sample_Requirements
*
* @group requirements
*/
class Test_Sample_Requirements extends TestCase {
/**
* Test if is_php_version_required().
*/
public function test_is_php_version_required() {
$subject = new Sample_Requirements();
FunctionMocker::replace( 'phpversion', '5.5' );
$this->assertFalse( $subject->is_php_version_required() );
FunctionMocker::replace( 'phpversion', '5.6' );
$this->assertTrue( $subject->is_php_version_required() );
}
}
Кроме того, FunctionMocker
предоставляет много дополнительных возможностей: замену статических методов, «подглядывание» за методами, замену глобальных переменных и методов глобальных объектов ( например, $wpdb->get_row
) и т.д.