2026年9月24日

2026年9月24日

WordPressプラグインのPHPUnit統合テストの書き方(wp scaffoldとWP_UnitTestCase)

WordPress のプラグインを PHPUnit でテストするとき、WordPress 本体を読み込んだ状態で関数やフック、データベースの動きまで確かめるのが「統合テスト(integration test)」です。WordPress 本体のテストスイートに含まれる WP_UnitTestCase を使うと、テストごとにデータベースの変更が巻き戻され、投稿やユーザーをファクトリーで手早く作れます。

この記事では、WP-CLI で雛形を作り、テスト用の WordPress を用意し、テストを書いて実行するまでを説明します。手順とコードは、WordPress 6.9、PHP 8.3、MySQL 8.4、PHPUnit 9.6 の組み合わせで実際にテストが通ることを確認しています(2026年10月時点)。

用意するもの

  • PHP(WordPress の動作要件を満たす版)と Composer
  • WP-CLI
  • MySQL または MariaDB(テスト専用のデータベースを作れること)
  • curl または wget、bash

テスト用のデータベースは、テストのたびに中身が消されます。本番や開発で使っているデータベースを指定しないでください。

PHPUnitのバージョン

WordPress のテストスイートは、PHPUnit のバージョン差を「PHPUnit Polyfills」(yoast/phpunit-polyfills)で吸収しています。2026年10月時点の WordPress 本体の開発用 composer.json は Polyfills の ^1.1.0 を指定しており、Polyfills 1.x が対応する PHPUnit は 9 系までです。プラグインのテストでも、PHPUnit 9.6 と Polyfills 1.x の組み合わせを使うのが無難です。

プラグインのフォルダーで、開発用の依存として入れます。

cd wp-content/plugins/my-plugin
composer require --dev "phpunit/phpunit:^9.6" "yoast/phpunit-polyfills:^1.1"

手順1:テストの雛形を作る

WP-CLI の wp scaffold plugin-tests で、テストに必要なファイルを生成します。WordPress のインストール先で実行します。

wp scaffold plugin-tests my-plugin

生成される主なファイルは次のとおりです。

ファイル 役割
bin/install-wp-tests.sh テスト用の WordPress とテストスイートを取得し、テスト用データベースを用意する
tests/bootstrap.php テストスイートを読み込み、テスト対象のプラグインを有効化する
tests/test-sample.php テストの見本
phpunit.xml.dist PHPUnit の設定(tests/ の test- で始まるファイルを実行)
.phpcs.xml.dist PHP_CodeSniffer のルール
.circleci/config.yml CI の設定(--ci=github などで GitHub Actions 用に変更可)

tests/bootstrap.php は、muplugins_loaded の時点でプラグインのメインファイルを読み込むように書かれています。メインファイル名がプラグインのフォルダー名と違う場合は、この中のパスを確認してください。

手順2:テスト用のWordPressを用意する

bin/install-wp-tests.sh に、データベース名、ユーザー、パスワード、ホスト、WordPress のバージョンを渡します。

bash bin/install-wp-tests.sh wordpress_test root 'パスワード' localhost latest
引数 例 意味
1 wordpress_test テスト用データベース名(中身は消される)
2 root データベースユーザー
3 'パスワード' パスワード(空なら '')
4 localhost ホスト(省略時 localhost)
5 latest WordPress のバージョン(省略時 latest)
6 true データベースを作らない(既に作ってある場合。省略時 false)

既定では、WordPress 本体が /tmp/wordpress、テストスイートが /tmp/wordpress-tests-lib に置かれます(環境変数 TMPDIR や WP_TESTS_DIR で変更可)。データベースを作る処理では mysqladmin コマンドを使うため、MySQL のクライアントが入っていない環境では、先にデータベースを作っておき、6番目の引数に true を渡します。

/tmp はサーバーの再起動で消えることがあります。テストが「/tmp/wordpress-tests-lib/includes/functions.php が見つからない」と止まったら、このスクリプトを再実行してください。

手順3:テスト対象のコード

例として、次のプラグインをテストします。「書籍」投稿タイプの登録、公開済みの書籍のタイトルを返す関数、書籍のページで本文に注記を足すフィルターです。

<?php
/**
 * Plugin Name: My Plugin
 */

// 「書籍」投稿タイプを登録する
function my_plugin_register_book() {
	register_post_type(
		'book',
		array(
			'label'        => '書籍',
			'public'       => true,
			'show_in_rest' => true,
		)
	);
}
add_action( 'init', 'my_plugin_register_book' );

// 公開済みの書籍のタイトルを新しい順に返す
function my_plugin_recent_book_titles( $limit = 5 ) {
	$posts = get_posts(
		array(
			'post_type'      => 'book',
			'post_status'    => 'publish',
			'posts_per_page' => $limit,
			'orderby'        => 'date',
			'order'          => 'DESC',
		)
	);
	return wp_list_pluck( $posts, 'post_title' );
}

// 書籍の本文の末尾に注記を付ける
function my_plugin_append_note( $content ) {
	if ( is_singular( 'book' ) && in_the_loop() && is_main_query() ) {
		$content .= '<p class="book-note">価格は変わることがあります。</p>';
	}
	return $content;
}
add_filter( 'the_content', 'my_plugin_append_note' );

手順4:統合テストを書く

tests/test-book.php を作ります。テストクラスは PHPUnit の TestCase ではなく、WordPress の WP_UnitTestCase を継承します。

<?php
/**
 * 書籍機能の統合テスト。
 */
class Test_Book extends WP_UnitTestCase {

	public function test_post_type_is_registered() {
		$this->assertTrue( post_type_exists( 'book' ) );
		$this->assertTrue( get_post_type_object( 'book' )->show_in_rest );
	}

	public function test_recent_titles_returns_only_published_books() {
		self::factory()->post->create(
			array(
				'post_type'  => 'book',
				'post_title' => '古い本',
				'post_date'  => '2026-01-01 00:00:00',
			)
		);
		self::factory()->post->create(
			array(
				'post_type'  => 'book',
				'post_title' => '新しい本',
				'post_date'  => '2026-02-01 00:00:00',
			)
		);
		self::factory()->post->create(
			array(
				'post_type'   => 'book',
				'post_title'  => '下書きの本',
				'post_status' => 'draft',
			)
		);
		self::factory()->post->create( array( 'post_title' => '普通の投稿' ) );

		$this->assertSame( array( '新しい本', '古い本' ), my_plugin_recent_book_titles() );
	}

	public function test_note_is_appended_on_single_book() {
		$book_id = self::factory()->post->create(
			array(
				'post_type'    => 'book',
				'post_content' => '本文',
			)
		);

		$this->go_to( get_permalink( $book_id ) );
		$this->assertTrue( is_singular( 'book' ) );

		the_post();
		$html = apply_filters( 'the_content', get_the_content() );

		$this->assertStringContainsString( 'book-note', $html );
	}

	public function test_books_are_listed_in_rest_api() {
		self::factory()->post->create(
			array(
				'post_type'  => 'book',
				'post_title' => 'REST の本',
			)
		);

		$request  = new WP_REST_Request( 'GET', '/wp/v2/book' );
		$response = rest_get_server()->dispatch( $request );

		$this->assertSame( 200, $response->get_status() );
		$this->assertSame( 'REST の本', $response->get_data()[0]['title']['rendered'] );
	}
}

WP_UnitTestCaseで使える主な機能

  • ファクトリー: self::factory()->post->create() で投稿を作り、ID を返します。user・term・comment・attachment なども同じ形で作れます。
  • データの巻き戻し: 各テストはデータベースのトランザクションの中で実行され、終わると巻き戻されます。テストどうしでデータが混ざらないため、テストの順番に依存しない書き方ができます。
  • 画面の状態の再現: $this->go_to( URL ) で、その URL を開いたときのメインクエリ(is_singular() などの条件分岐)を再現できます。
  • REST API: WP_REST_Request を作って rest_get_server()->dispatch() に渡すと、HTTP を使わずにエンドポイントを呼べます。
  • 準備と後片付け: テストごとの準備は set_up()、後片付けは tear_down() に書き、最初に parent::set_up()(parent::tear_down())を呼びます。PHPUnit 本来の setUp() ではなく、スネークケースの名前を使うのが WordPress のテストスイートの決まりです。
public function set_up() {
	parent::set_up();
	$this->admin_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
	wp_set_current_user( $this->admin_id );
}

手順5:テストを実行する

プラグインのフォルダーで、Composer で入れた PHPUnit を実行します。

vendor/bin/phpunit

成功すると、次のように表示されます。

PHPUnit 9.6.x by Sebastian Bergmann and contributors.

....                                                                4 / 4 (100%)

OK (4 tests, 7 assertions)

特定のテストだけ実行したいときは --filter を使います。

vendor/bin/phpunit --filter test_books_are_listed_in_rest_api

よくあるエラー

  1. 「Could not find /tmp/wordpress-tests-lib/includes/functions.php」: 手順2のスクリプトを実行していないか、/tmp が消えています。再実行してください。
  2. 「The PHPUnit Polyfills library is a requirement」: Polyfills が読み込まれていません。composer require --dev で入れたうえで vendor/bin/phpunit から実行するか、環境変数 WP_TESTS_PHPUNIT_POLYFILLS_PATH に vendor/yoast/phpunit-polyfills のフルパス(/ から始まるパス)を設定します。
  3. 「Error establishing a database connection」: /tmp/wordpress-tests-lib/wp-tests-config.php のデータベース設定を確認します。スクリプトは既存の設定ファイルを上書きしないため、接続情報を変えたときはこのファイルを直接直すか、削除して再実行します。
  4. テストは通るのに tests/test-sample.php が実行されない: 生成された phpunit.xml.dist で、見本のファイルは実行対象から外されています。自分のテストは test- で始まるファイル名にしてください。
  5. PHPUnit 10 以降でエラーになる: 上の「PHPUnitのバージョン」のとおり、9 系に戻します。

単体テストとの使い分け

統合テストは WordPress 全体を読み込むため、実行に数秒以上かかり、データベースも必要です。WordPress の関数に依存しない計算や文字列処理は、WordPress を読み込まない単体テストに分けると速く回せます。Docker で WordPress の開発環境を作る wp-env を使ってテストを動かす方法は、関連記事で説明しています。

まとめ

  • wp scaffold plugin-tests で雛形を作り、bin/install-wp-tests.sh でテスト用の WordPress とデータベースを用意する。
  • テスト用データベースは中身が消されるため、専用のものを使う。
  • テストクラスは WP_UnitTestCase を継承し、ファクトリー、go_to()、WP_REST_Request で WordPress の動きを確かめる。
  • PHPUnit は 9.6 と Polyfills 1.x の組み合わせで、vendor/bin/phpunit から実行する。

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲