2026年9月3日
2026年9月3日
WordPressプラグインにPHPUnitでユニットテストを追加する方法
はじめに
WordPressプラグインのコードにバグが多くユニットテストを追加したい・WP_UnitTestCaseの使い方がわからない・テスト用のWordPressインストールのセットアップがわからない・GitHub ActionsでCIパイプラインを構築したいといった問題の解決方法を解説します。
症状・原因
- WordPress用のテスト環境(
wp-tests-config.php)が設定されていない WP_UnitTestCaseを継承しているが各テストの前後でDBがリセットされないと思い込んでいるsetUp()とtearDown()の中でWordPressの状態が汚染されて後続テストが失敗する- モックオブジェクトの設定が間違っていてフックが正しくテストできない
解決手順
ステップ1:テスト環境をセットアップする
# ✅ プラグインディレクトリに移動
cd /var/www/html/wp-content/plugins/my-plugin
# ✅ WP-CLI でテストスキャフォールドを生成(推奨)
wp scaffold plugin-tests my-plugin --path=/var/www/html/
# → tests/
# → bootstrap.php ← テスト起動ファイル
# → test-sample.php ← サンプルテスト
# → phpunit.xml.dist ← PHPUnit設定
# → bin/
# → install-wp-tests.sh ← テストDB作成スクリプト
# ✅ テスト用データベースを作成してWordPressをインストール
bash bin/install-wp-tests.sh wordpress_test root password localhost latest
# → テスト用のWordPressがインストールされる
# → 通常のサイトとは別のDB(wordpress_test)を使用
# ✅ PHPUnit をインストール
composer require --dev phpunit/phpunit:^10 brain/monkey:^2.6
# ✅ phpunit.xml.dist の確認
cat phpunit.xml.dist
# → <testsuites>
# → <testsuite name="WP Plugin Tests">
# → <directory suffix="Test.php">./tests</directory>
# → </testsuite>
# → </testsuites>
# ✅ テストを実行
./vendor/bin/phpunit
# → PHPUnit 10.x.x
# → . → テスト成功
# → 1 test, 1 assertion
ステップ2:WP_UnitTestCaseを使ったテストを作成する
// ✅ tests/test-plugin.php
class MyPluginTest extends WP_UnitTestCase {
// ✅ 各テストの前に実行(DBはWP_UnitTestCaseが自動リセット)
public function setUp(): void {
parent::setUp();
// テスト用のセットアップ
update_option('my_plugin_enabled', true);
}
public function tearDown(): void {
parent::tearDown();
// テスト後のクリーンアップ
delete_option('my_plugin_enabled');
}
// ✅ 投稿が正しく作成されるかテスト
public function test_create_post(): void {
$post_id = wp_insert_post([
'post_title' => 'Test Post',
'post_status' => 'publish',
'post_type' => 'post',
]);
$this->assertIsInt($post_id);
$this->assertGreaterThan(0, $post_id);
$this->assertEquals('Test Post', get_the_title($post_id));
}
// ✅ フックが正しく動作するかテスト
public function test_hook_is_registered(): void {
$plugin = new \MyCompany\MyPlugin\Plugin();
$plugin->initialize();
$this->assertEquals(10, has_action('init', [$plugin, 'setup']));
}
// ✅ オプションが正しく保存されるかテスト
public function test_save_settings(): void {
$settings = new \MyCompany\MyPlugin\Admin\Settings();
$settings->save(['option_key' => 'test_value']);
$this->assertEquals('test_value', get_option('my_plugin_option_key'));
}
}
ステップ3:モックとスタブを使ったテスト
// ✅ Brain\Monkey を使ったWordPress関数のモック
use Brain\Monkey;
use Brain\Monkey\Functions;
use Brain\Monkey\Actions;
class MyPluginWithMockTest extends \PHPUnit\Framework\TestCase {
protected function setUp(): void {
parent::setUp();
Monkey\setUp(); // ← Brain\Monkey の初期化
}
protected function tearDown(): void {
Monkey\tearDown(); // ← 後始末
parent::tearDown();
}
// ✅ WordPress 関数をモック(データベース不要でテスト可能)
public function test_get_post_title_with_mock(): void {
Functions\when('get_the_title')
->justReturn('Mocked Post Title');
Functions\when('get_post')
->justReturn((object)['ID' => 1, 'post_title' => 'Mocked Post Title']);
$my_class = new \MyCompany\MyPlugin\Frontend\PostHelper();
$result = $my_class->getTitle(1);
$this->assertEquals('Mocked Post Title', $result);
}
// ✅ フックが実行されたかテスト
public function test_action_is_triggered(): void {
Actions\expectDone('my_plugin_action')
->once()
->with('expected_argument');
do_action('my_plugin_action', 'expected_argument');
}
// ✅ PHPUnit のモックオブジェクト
public function test_api_call_with_mock(): void {
$mock_http = $this->createMock(\MyCompany\MyPlugin\API\HttpClient::class);
$mock_http->method('get')
->willReturn(['status' => 200, 'data' => ['key' => 'value']]);
$service = new \MyCompany\MyPlugin\API\DataService($mock_http);
$result = $service->fetchData('endpoint');
$this->assertEquals('value', $result['key']);
}
}
ステップ4:データプロバイダーと例外テスト
// ✅ データプロバイダーで複数のケースをまとめてテスト
class ValidationTest extends WP_UnitTestCase {
/**
* @dataProvider validEmailProvider
*/
public function test_valid_email(string $email, bool $expected): void {
$validator = new \MyCompany\MyPlugin\Utils\Validator();
$this->assertEquals($expected, $validator->isValidEmail($email));
}
public static function validEmailProvider(): array {
return [
'valid email' => ['test@example.com', true],
'invalid email' => ['not-an-email', false],
'empty string' => ['', false],
'japanese domain' => ['test@example.co.jp', true],
];
}
// ✅ 例外がスローされることをテスト
public function test_throws_exception_for_invalid_post(): void {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid post ID');
$handler = new \MyCompany\MyPlugin\Frontend\PostHelper();
$handler->getTitle(0); // 無効なID
}
// ✅ DBトランザクションのテスト(ロールバック確認)
public function test_order_creation_rolls_back_on_error(): void {
$initial_count = $this->factory->post->count();
try {
(new \MyCompany\MyPlugin\Orders\OrderService())->createWithInvalidData([]);
} catch (\Exception $e) {
// 例外後もDBは変更されていないことを確認
$this->assertEquals($initial_count, $this->factory->post->count());
}
}
}
ステップ5:GitHub ActionsでCIを設定する
# ✅ .github/workflows/tests.yml を作成
mkdir -p .github/workflows
cat > .github/workflows/tests.yml << 'EOF'
name: WordPress Plugin Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: [8.0, 8.1, 8.2]
wordpress: ['6.4', 'latest']
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: wordpress_test
options: --health-cmd="mysqladmin ping" --health-timeout=5s
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mysql, mbstring, zip
- name: Install Composer dependencies
run: composer install --no-interaction --prefer-dist
- name: Install WordPress test suite
run: |
bash bin/install-wp-tests.sh wordpress_test root password 127.0.0.1 ${{ matrix.wordpress }}
- name: Run PHPUnit tests
run: ./vendor/bin/phpunit --coverage-text
EOF
# ✅ カバレッジレポートを生成
./vendor/bin/phpunit --coverage-html tests/coverage/
# → tests/coverage/index.html でカバレッジを確認
# ✅ テスト結果の確認
./vendor/bin/phpunit --testdox
# → MyPlugin (MyPluginTest)
# → ✔ Create post
# → ✔ Hook is registered
# → ✔ Save settings
注意事項
WP_UnitTestCaseは各テスト後にデータベースのトランザクションをロールバックするため、テスト間でデータが汚染されません。ただし$wpdb->query()を直接使ったDDL文(CREATE TABLE等)はロールバックされません- CIでテストを実行する際、
bin/install-wp-tests.shはインターネットからWordPressをダウンロードします。キャッシュを活用して実行時間を短縮するために、GitHubActionsのactions/cacheを使ってWordPressのインストールをキャッシュしてください
まとめ
PHPUnitでのWordPressプラグインテストは①wp scaffold plugin-testsでテストスキャフォールド生成・bin/install-wp-tests.shでテスト用DB作成・composer require --dev phpunit/phpunitでPHPUnitインストール、②WP_UnitTestCaseを継承・setUp()/tearDown()でテストの前後処理・$this->factory->post->create()でテストデータ生成、③Brain\Monkeyでget_the_title()等のWordPress関数をモック・Actions\expectDone()でフックの実行を検証、④@dataProviderで複数ケースをまとめてテスト・$this->expectException()で例外テスト・ファクトリーでDBのロールバックを確認、⑤GitHub Actionsのmatrix戦略でPHP/WordPress複数バージョンをテスト・カバレッジレポート生成の手順で実装します。