Модульное тестирование WordPress с помощью Brain Monkey
Кто еще не знаком с тестированием и модульным тестированием можете ознакомится: Автоматизация тестирования, Модульное тестирование с помощью PHPUnit.
Тестирование тем и плагинов под WordPress имеет одну большую проблему — взаимодействие с ядром. Решить ее можно с помощью библиотек Brain-WP/BrainMonkey или 10up/WP_Mock.
Как писать тесты с помощью 10up/WP_Mock вы можете прочитать в статье: Модульное тестирование WordPress (PHPUnit, WP_Mock), но а сейчас разберемся с Brain Monkey.
Библиотека Brain Monkey помогает делать заглушки для функций и классов из ядра WordPress.
Установка библиотеки Brain-WP/BrainMonkey для тестирования WordPress
Устанавливаем библиотеку через composer:
composer require --dev brain/monkey
Для работы библиотеки нужно использовать фикстуры setUp
и tearDown
:
use Brain\Monkey; use PHPUnit\Framework\TestCase; class Test_Main extends TestCase { public function setUp(): void { parent::setUp(); Monkey\setUp(); } public function tearDown(): void { Monkey\tearDown(); parent::tearDown(); } }
Теперь мы можем делать заглушки абсолютно для любых функций и классов WordPress.
Возможности библиотеки
Brain\Monkey\Functions\when;
Данная функция нужна для того, чтобы при вызове функции вернуть какой-то результат.
justReturn
use function Brain\Monkey\Functions\when; when( 'function_name' )->justReturn( 'krya' ); $this->assertSame( 'krya', function_name() );
При помощи when
мы делаем мок для функции function_name
и при ее вызове будет возвращен результат из метода justReturn
.
returnArg
when( 'func1' )->returnArg(); when( 'func2' )->returnArg( 2 ); when( 'func3' )->returnArg( 3 ); $this->assertSame( 'krya', func1( 'krya', 2, 3 ) ); $this->assertSame( 'krya', func2( 1, 'krya', 3 ) ); $this->assertSame( 'krya', funct3( 1, 2, 'krya' ) );
При помощи when
мы делаем мок для функции function_name
и при ее вызове будет возвращен аргумент функции под номером из метода returnArg
.
Это очень удобно, когда нам нужно использовать функции для очистки переменных или очистки данных перед выводом(sanitize_*, esc_*).
justEcho
when( 'function_name' )->justEcho( 'krya' ); ob_start(); function_name(); $this->assertSame( 'krya', ob_get_clean() );
При вызове функции function_name
выводим на экран текст из метода justEcho
.
alias
when( 'duplicate' )->alias( function ( $value ) { return "Was " . $value . ", now is " . ( $value * 2 ); } ); $this->assertSame( 'Was 1, now is 2', duplicate( 1 ) );
Вызов функции duplicate
мы заменяем на нашу функцию.
when( 'bigger' )->alias( 'strtoupper' ); $this->assertSame( 'WAS LOWER', bigger( 'was lower' ) );
При вызове функции bigger
будет вызвана функция strtoupper
.
Brain\Monkey\Functions\stubs;
Функция для массовой замены функций:
stubs( [ 'esc_attr', 'esc_html', 'esc_textarea', '__', '_x', 'esc_html__', 'esc_html_x', 'esc_attr_x', ] ); $this->assertSame( 'krya', esc_attr( 'krya' ) ); $this->assertSame( 'krya', esc_html( 'krya' ) );
При вызове каждой из функций будет возвращаться первый аргумент, который был в нее передан. Тоже самое что и when( 'func1' )->returnArg()
, но массово.
Так же можно вторым параметром указать результат функций:
stubs( [ 'is_user_logged_in', 'current_user_can', ], true );
Или еще круче, передав массив ключ-значени, в котором ключ — название функции, а значении примитив или callback.
stubs( [ 'is_user_logged_in' => true, 'current_user_can' => false, 'bigger' => 'strtoupper', ] ); $this->assertTrue( is_user_logged_in() ); $this->assertFalse( current_user_can() ); $this->assertSame( 'WAS LOWER', bigger( 'was lower' ) );
Подводные камни
null
нельзя передать, как результат функции, поэтому используйте __return_null
для этого:
stubs( [ 'return_null_function' => '__return_null', ] ); $this->assertNull( return_null_function() );
Если вам нужно замокать функцию, которая возвращает callback
. Поэтому нужно указать callback
, который возвращает callback
.

stubs( [ 'return_callback' => function () { return [ 'Callback_Class', 'callback_method' ]; }, ] ); $this->assertSame( [ 'Callback_Class', 'callback_method' ], return_callback() );
Brain\Monkey\Functions\expect
Основной инструмент для заглушек функций. В нем можно указать сколько раз, с какими аргументами и что вернула функция.
Функция была вызвана n-раз
expect( 'once' )->once(); // 1 expect( 'twice' )->twice(); // 2 expect( 'i_dont_know' )->zeroOrMoreTimes(); expect( 'no_more_than_one' )->atLeast()->once(); // <=1 expect( 'more_than_one' )->atMost()->once(); // >= 1 expect( 'never' )->never(); // 0 expect( 'three_times' )->times( 3 ); // 3 expect( 'from_2_to_4_times' )->between( 2, 4 ); // 2-4
Функция была вызвана с такими параметрами:
expect( 'function_name' )->once()->withAnyArgs(); function_name( 1, 2, 3 ); expect( 'function_name' )->once()->withNoArgs(); function_name(); expect( 'function_name' )->once()->with( 'arg1', 'arg2' ); function_name( 'arg1', 'arg2' ); expect( 'function_name' ) ->once() ->with( Mockery::type( 'int' ), Mockery::type( 'string' ) ); function_name( 10, 'string' ); expect( 'function_name' )->once()->with( Mockery::any() ); function_name( new stdClass() ); expect( 'function_name' )->once()->with( Mockery::anyOf( 'a', 2, true ) ); function_name( 'a' ); expect( 'function_name' )->once()->with( Mockery::not( 'a', 2, true ) ); function_name( 1 );
withAnyArgs()
— с любыми аргументами, с любым их количеством или вовсе без них;withNoArgs()
— без аргументов;with( ... )
— указываем точные аргументы.with( Mockery::type( 'int' ), Mockery::type( 'string' ) )
— первый аргумент любое число, а второй — любая строка;with( Mockery::any() )
— первый аргумент может быть абсолютно любого типа;with( Mockery::anyOf( 'a', 2, true ) )
— первый аргумент любой из списка;with( Mockery::not( 'a', 2, true ) )
— любой, кроме тех, что в списке.
Функция возвращает
expect( 'function_name' )->once()->andReturn( 'Baz!' ); $this->assertSame( 'Baz!', function_name() ); expect( 'function_name' ) ->twice() ->andReturn( 'First time I run', 'Second time I run' ); $this->assertSame( 'First time I run', function_name( 'First time I run' ) ); $this->assertSame( 'Second time I run', function_name( 'Second time I run' ) ); expect( 'function_name' ) ->twice() ->andReturnValues( [ 'First time I run', 'Second time I run' ] ); $this->assertSame( 'First time I run', function_name( 'First time I run' ) ); $this->assertSame( 'Second time I run', function_name( 'Second time I run' ) ); expect( 'function_name' ) ->once() ->andReturnNull(); $this->assertNull( function_name() ); expect( 'function_name' ) ->once() ->andReturnUsing( function () { return 'I am an alias!'; } ); $this->assertSame( 'I am an alias!', function_name() ); expect( 'function_name' ) ->once() ->andThrow( 'RuntimeException' ); $this->expectException( 'RuntimeException' ); function_name();
andReturn( 'Baz!' )
— функция вернет Baz!;andReturn( 'One', 'Two' )
— функция вернет One в первый раз и Two во второй;andReturnValues( 'One', 'Two' )
— функция вернет One в первый раз и Two во второй;andReturnNull()
— функция вернет null;andReturnUsing( callback )
— функция вернет callback;andThrow( 'RuntimeException' )
— функция выбросит исключениеRuntimeException
.
Тестирование хуков
Тестирование подключений хуков
Тестируемый класс:
class Metabox { public function hooks() { add_action( 'init', [ $this, 'init' ], 20, 4 ); add_filter( 'the_title', [ $this, 'the_title' ], 20, 2 ); } public function init() { } public function the_title() { } }
Проверить подключение хуков можно с помощью функций has_action
и has_filter
:
public function test_hooks() { $metabox = new Metabox(); $metabox->hooks(); $this->assertTrue( has_action( 'init', [ $metabox, 'init' ] ) ); $this->assertTrue( has_filter( 'the_title', [ $metabox, 'the_title' ] ) ); }
Тестирование наличия хуков
Пример класса:
class Metabox { public function awesome_method() { do_action( 'my_action', $this ); return apply_filters( 'my_filter', 'Filter applied', $this ); } }
Проверяем с помощью did_action
и applied
, которые возвращают кол-во вызовов данных хуков:
public function test_awesome_method() { $metabox = new Metabox(); $this->assertNull( $metabox->awesome_method() ); $this->assertSame( 1, did_action( 'my_action' ) ); $this->assertSame( 1, applied( 'my_filter' ) ); }
Как видите из прошлого примера, то мы не проверили, с какими параметрами вызван хук и для фильтра не подменили результат.
Поэтому тестировать лучше и более качественее с помощью Brain\Monkey\Actions\expectDone
и Brain\Monkey\Filters\expectApplied
:
public function test_awesome_method() { $metabox = new Metabox(); expectDone( 'my_action' ) ->once() ->with( $metabox ); expectApplied( 'my_filter' ) ->once() ->with( 'Filter applied', $metabox ) ->andReturn( 'Brain Monkey rocks!' ); $metabox->awesome_method(); }
Вывод
С помощью Brain Monkey мы можем тестировать большую часть функциональности WordPress. А как вы считаете что лучше WP_Mock или Brain Monkey?
Источник: WP Punk.