単独でテストする
プラグインを開発する場合、WordPress環境を読み込まずにテストするのが最善の方法です。
WordPressなしで簡単にテストできるコードを書くと、コードはより良くなります。
単体テストされるすべてのコンポーネントは、単独でテストする必要があります。クラスをテストするときは、他のすべてのコードが完全に機能していると仮定して、その特定のクラスをテストするだけです。
これが、単体テストが「ユニット」と呼ばれる理由です。
追加の利点として、コアをロードしなくても、テストがはるかに高速に実行されます。
コンストラクターでのフックを避ける
私があなたに与えることができるヒントは、コンストラクターにフックを置くことを避けることです。これは、コードを単独でテスト可能にすることの1つです。
OPでテストコードを見てみましょう。
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
。
上記のクラスには、テストするものが2つあります。
- メソッドは
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' ) );
}
}
次のことに注意してください。
- 2つのメソッドを別々に呼び出すことができたため、WordPressはまったくロードされません。このようにして、1つのテストが失敗した場合、犯人が誰であるかを正確に知ることができます。
- 先ほど言ったように、ここではクラスが期待される引数でWP関数を呼び出すことをテストします。CPTが実際に存在するかどうかをテストする必要はありません。CPTの存在をテストする場合、プラグインの動作ではなく、WordPressの動作をテストしています...
素敵な..しかし、それはピタです!
はい、手動ですべてのWordPress機能をモックする必要がある場合、それは本当に苦痛です。私ができるいくつかの一般的なアドバイスは、できるだけ少ないWP関数を使用することです:WordPress を書き換える必要はありませんが、カスタムクラスで使用する抽象 WP関数は、それらを模擬し、簡単にテストできるようにします。
たとえば、上記の例に関して、投稿タイプを登録するクラスを作成して、呼び出すことができます register_post_type
、与えられた引数で 'init'をてます。この抽象化では、そのクラスをテストする必要がありますが、投稿タイプを登録するコードの他の場所では、そのクラスを使用して、テストでモックすることができます(したがって、動作すると仮定します)。
素晴らしいところは、CPT登録を抽象化するクラスを書いた場合、あなたはそれのために別々のリポジトリを作成することができ、ある、など近代的なツールのおかげComposerは、あなたがそれを必要とするすべてのプロジェクトでそれを埋め込む:テストどこでも使用し、一回。また、バグを見つけた場合は、1か所で修正することができ、簡単な方法で、composer update
使用されているすべてのプロジェクトも修正されます。
2回目:単独でテスト可能なコードを記述することは、より良いコードを記述することを意味します。
しかし、遅かれ早かれ、どこかでWP関数を使用する必要があります...
もちろん。コアと並行して行動してはいけません。意味がありません。WP関数をラップするクラスを作成できますが、それらのクラスもテストする必要があります。上記の「手動」メソッドは、非常に単純なタスクに使用できますが、クラスに多くのWP関数が含まれている場合は苦痛になります。
幸いなことに、そこには良いことを書く良い人がいます。最大のWPエージェンシーの1つである10upは、プラグインを正しい方法でテストしたい人のための非常に優れたライブラリを維持しています。それは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
ので、詳細についてはレポのreadmeを読んでください。しかし、上の例はかなり明確なはずです。
さらに、モックadd_action
やregister_post_type
自分で作成したり、グローバル変数を管理したりする必要はありません 。
そしてWPクラス?
WPにはいくつかのクラスもあり、テストの実行時にWordPressがロードされない場合は、それらをモックする必要があります。
これは関数をモックするよりもはるかに簡単です。PHPUnitにはオブジェクトをモックする組み込みシステムがありますが、ここではMockeryを提案します。非常に強力なライブラリであり、非常に使いやすいです。さらに、それはの依存関係ですWP_Mock
ので、持っている場合はMockeryもあります。
しかし、どうWP_UnitTestCase
ですか?
WordPressテストスイートは、WordPress コアをテストするために作成されたもので、コアに貢献したい場合は極めて重要ですが、プラグインに使用することで、単独でテストすることができません。
WPの世界に目を向けてください。最新のPHPフレームワークとCMSは数多くあり、フレームワークコードを使用してプラグイン/モジュール/拡張機能(またはそれらが呼び出されるもの)をテストすることを提案するものはありません。
スイートの便利な機能である工場を見逃した場合、素晴らしいものがあることを知っておく必要がありますあります。
落とし穴と欠点
ここで提案したワークフローにない場合があります:カスタムデータベーステスト。
実際、標準のWordPressのテーブルと関数を使用してそこに(最低レベルの$wpdb
メソッドで)書き込む場合、実際にデータを書き込んだり、データが実際にデータベースにあるかどうかをテストしたりする必要はありません。適切な引数で適切なメソッドが呼び出されることを確認してください。
ただし、カスタムテーブルとプラグインを作成して、そこに書き込むクエリを作成し、それらのクエリが機能するかどうかをテストすることができます。
このような場合、WordPressテストスイートは非常に役立ちます。また、次のような機能を実行するためにWordPressのロードが必要になる場合があります。 dbDelta
。
(テストに別のデータベースを使用する必要はありませんよね?)
幸いにもPHPUnitは、あなたのテストのすべての残り残してあなたはWordPressの環境(またはその一部)をロードするカスタムデータベースのテストのためのスイートを書くことができますので、あなたは、個別に実行することができ、「スイート」でテストを整理することができますワードプレスフリーを。
モックを使用すると、データベースを処理せずに大部分のクラスを適切にテストできるように、できるだけ多くのデータベース操作を抽象化するクラスを作成してください。
第三に、単独で簡単にテスト可能なコードを書くことは、より良いコードを書くことを意味します。
phpunit
、失敗または合格したテストを確認できますか?インストールしましたbin/install-wp-tests.sh
か?