Випробування ізольовано
Розробляючи плагін, найкращий спосіб перевірити його - не завантажуючи середовище WordPress.
Якщо ви пишете код, який можна легко перевірити без WordPress, ваш код стане кращим .
Кожен компонент, який перевіряється одиницею, повинен бути перевірений ізольовано : коли ви тестуєте клас, вам потрібно лише протестувати цей конкретний клас, припускаючи, що всі інші коди працюють ідеально.
З цієї причини одиничні тести називаються "одиницями".
В якості додаткової переваги, не завантажуючи core, ваш тест запуститься набагато швидше.
Уникайте гачків у конструкторі
Порада, яку я можу дати вам, - це не уникати гачків у конструкторах. Це одна з речей, яка зробить ваш код перевіреним у відриві.
Давайте подивимось тестовий код в ОП:
class CustomPostTypes extends WP_UnitTestCase {
function test_custom_post_type_creation() {
$this->assertTrue( post_type_exists( 'foo' ) );
}
}
І припустимо, що цей тест не вдається . Хто винуватець ?
- гачок не був доданий зовсім чи неправильно?
- метод, який реєструє тип публікації, взагалі не викликався або з помилковими аргументами?
- є помилка в WordPress?
Як це можна вдосконалити?
Припустимо, ваш клас класу:
class RegisterCustomPostType {
function init() {
add_action( 'init', array( $this, 'register_post_type' ) );
}
public function register_post_type() {
register_post_type( 'foo' );
}
}
(Примітка. За іншою відповіддю я посилаюся на цю версію класу)
Те, як я написав цей клас, дозволяє створювати екземпляри класу без виклику add_action
.
У вищевказаному класі є дві речі, які потрібно перевірити:
- метод
init
фактично викликає add_action
передачу йому належних аргументів
- метод
register_post_type
фактично викликає register_post_type
функцію
Я не казав, що вам потрібно перевірити, чи існує тип публікації: якщо ви додасте належну дію і якщо ви телефонуєте register_post_type
, повинен існувати спеціальний тип пошти : якщо його немає, це проблема WordPress.
Пам'ятайте: коли ви перевіряєте свій плагін, ви повинні перевірити свій код, а не WordPress-код. У своїх тестах ви повинні припустити, що WordPress (як і будь-яка інша зовнішня бібліотека, яку ви використовуєте) працює добре. Ось сенс одиничного тесту.
Але ... на практиці?
Якщо WordPress не завантажений, якщо ви намагаєтеся викликати методи класу вище, ви отримуєте фатальну помилку, тому вам потрібно знущатися над функціями.
Метод "вручну"
Впевнені, що ви можете написати свою глузувальну бібліотеку або "вручну" знущатися над кожним методом. Це можливо. Я розповім, як це зробити, але тоді я покажу вам більш простий метод.
Якщо WordPress не завантажується під час запуску тестів, це означає, що ви можете переглянути свої функції, наприклад, add_action
або register_post_type
.
Припустимо, у вас є файл, завантажений із завантажувального файлу, де у вас є:
function add_action() {
global $counter;
if ( ! isset($counter['add_action']) ) {
$counter['add_action'] = array();
}
$counter['add_action'][] = func_get_args();
}
function register_post_type() {
global $counter;
if ( ! isset($counter['register_post_type']) ) {
$counter['register_post_type'] = array();
}
$counter['register_post_type'][] = func_get_args();
}
Я переписав функції, щоб просто додавати елемент у глобальний масив щоразу, коли вони викликаються.
Тепер ви повинні створити (якщо у вас його ще немає) розширювати власний базовий клас тестових випадків PHPUnit_Framework_TestCase
: це дозволяє легко налаштувати ваші тести.
Це може бути щось на кшталт:
class Custom_TestCase extends \PHPUnit_Framework_TestCase {
public function setUp() {
$GLOBALS['counter'] = array();
}
}
Таким чином, перед кожним тестом глобальний лічильник скидається.
А тепер ваш тестовий код (я маю на увазі переписаний клас, який я розмістив вище):
class CustomPostTypes extends Custom_TestCase {
function test_init() {
global $counter;
$r = new RegisterCustomPostType;
$r->init();
$this->assertSame(
$counter['add_action'][0],
array( 'init', array( $r, 'register_post_type' ) )
);
}
function test_register_post_type() {
global $counter;
$r = new RegisterCustomPostType;
$r->register_post_type();
$this->assertSame( $counter['register_post_type'][0], array( 'foo' ) );
}
}
Ви повинні відзначити:
- Мені вдалося викликати два методи окремо, і WordPress взагалі не завантажений. Таким чином, якщо один тест не вдасться, я точно знаю , хто є винуватцем.
- Як я вже говорив, тут я перевіряю, що класи викликають функції WP із очікуваними аргументами. Немає необхідності перевіряти, чи дійсно CPT існує. Якщо ви тестуєте існування CPT, то ви тестуєте поведінку WordPress, а не поведінку плагіна ...
Приємно .. але це ПІТА!
Так, якщо вам доведеться вручну знущатися над усіма функціями WordPress, це справді біль. Деякі загальні поради, які я можу дати, - це використовувати якомога менше функцій WP: вам не доведеться переписувати WordPress, але абстрактні функції WP, які ви використовуєте в користувацьких класах, щоб їх можна було глузувати і легко перевіряти.
Наприклад, щодо прикладу, наведеного вище, ви можете написати клас, який реєструє типи публікацій, викликаючи register_post_type
"init" із заданими аргументами. З цією абстракцією вам все-таки потрібно протестувати цей клас, але в інших місцях вашого коду, які реєструють типи публікацій, ви можете використовувати цей клас, глузуючи з нього в тестах (тому припускаючи, що він працює).
Дивовижна річ, якщо ви пишете клас, який абстрагує реєстрацію CPT, ви можете створити для нього окремий сховище, і завдяки сучасним інструментам, як Composer, вбудовуйте його у всі проекти, де вам це потрібно: протестуйте один раз, використовуйте всюди . І якщо ви коли-небудь знайдете в ньому помилку, ви можете виправити її в одному місці і за допомогою простого composer update
всі проекти, де вона використовується, також виправлені.
Вдруге: писати код, який можна перевірити ізольовано, означає написати кращий код.
Але рано чи пізно мені потрібно десь використовувати функції WP ...
Звичайно. Ніколи не слід діяти паралельно з ядром, це не має сенсу. Ви можете писати класи, які охоплюють функції WP, але їх також потрібно перевірити. Описаний вище метод "вручну" може використовуватися для дуже простих завдань, але коли клас містить багато функцій WP, це може бути болем.
На щастя, там є хороші люди, які пишуть добрі речі. 10up , одне з найбільших агентств WP, підтримує дуже чудову бібліотеку для людей, які хочуть перевірити плагіни правильним шляхом. Це є WP_Mock
.
Це дозволяє знущатися з функцій WP гаками . Якщо припустити, що ви завантажили в свої тести (див. Репо readme) той самий тест, який я писав вище, стає:
class CustomPostTypes extends Custom_TestCase {
function test_init() {
$r = new RegisterCustomPostType;
// tests that the action was added with given arguments
\WP_Mock::expectActionAdded( 'init', array( $r, 'register_post_type' ) );
$r->init();
}
function test_register_post_type() {
// tests that the function was called with given arguments and run once
\WP_Mock::wpFunction( 'register_post_type', array(
'times' => 1,
'args' => array( 'foo' ),
) );
$r = new RegisterCustomPostType;
$r->register_post_type();
}
}
Просто, чи не так? Ця відповідь не є навчальним посібником WP_Mock
, тому читайте репо-ремед для отримання додаткової інформації, але приклад, наведений вище, повинен бути досить зрозумілим.
Більше того, вам не потрібно писати знущань add_action
або register_post_type
самостійно, або підтримувати глобальні змінні.
А WP-класи?
У WP також є кілька класів, і якщо WordPress не завантажується під час запуску тестів, вам потрібно знущатися над ними.
Це набагато простіше , ніж глузливі функції, PHPUnit має вбудовану систему для фіктивних об'єктів, але тут я хочу запропонувати знущання вам. Це дуже потужна бібліотека і дуже проста у використанні. Більше того, це залежність WP_Mock
, тому, якщо у вас є, у вас є і насмішки.
А як же WP_UnitTestCase
?
Тестовий набір WordPress був створений для тестування ядра WordPress , і якщо ви хочете внести свій внесок у ядро, він є основним, але використання його для плагінів змушує вас протестувати не поодиноко.
Погляньте на світ WP: там багато сучасних фреймворків PHP та CMS, і жоден з них не пропонує тестування плагінів / модулів / розширень (або як вони ще називаються) за допомогою рамкового коду.
Якщо ви сумуєте за фабриками, корисною особливістю набору, ви повинні знати, що там є дивовижні речі .
Потрапляють і недоліки
Існує випадок, коли робочого процесу, який я запропонував тут, бракує: тестування баз даних .
Насправді, якщо ви використовуєте стандартні таблиці та функції WordPress для запису туди ( $wpdb
методами найнижчого рівня ), вам ніколи не потрібно насправді записувати дані або перевіряти, чи дані насправді є в базі даних, просто переконайтеся, що належні методи викликаються належними аргументами.
Однак ви можете писати плагіни за допомогою спеціальних таблиць і функцій, які створюють запити для запису там, і перевірити, чи працюють ці запити, це ваша відповідальність.
У таких випадках WordPress тестовий набір може вам дуже допомогти, а завантаження WordPress може знадобитися в деяких випадках для запуску таких функцій dbDelta
.
(Немає потреби говорити про використання іншого db для тестів, чи не так?)
На щастя, PHPUnit дозволяє організовувати свої тести в "наборах", які можна запустити окремо, тому ви можете написати набір для тестів на замовлення баз даних, де ви завантажуєте середовище WordPress (або його частину), залишаючи всі решта ваших тестів WordPress-безкоштовно .
Тільки не забудьте написати класи, які абстрагують якомога більше операцій з базою даних таким чином, щоб усі інші класи плагінів використовували їх, так що за допомогою макетів ви можете належним чином перевірити більшість класів, не маючи справи з базою даних.
Втретє, написання коду, який легко перевіряється ізольовано, означає писати кращий код.
phpunit
, чи можете ви побачити невдалі чи пройдені тести? Ви встановилиbin/install-wp-tests.sh
?