WordPressのプラグイン開発時にテストコードを書いてテストを行いながら開発することで、バグを見つけやすくなる。機能追加や更新時に手動でテストは行うけど、すべて網羅することは到底できないため、どうしてもバグが混入してそれに気が付けないことがある。その点、テストコードを書いておけば、テストを実行するだけでしっかり機能が保たれていることを確認できるので便利。
もちろん、テストコードを書いてそれを仕様に合わせてメンテナンスしていくのにもコストがかかるのだけど、ある程度プロジェクトが大きくなったらテストコードを書いたほうがおそらく良い。最近では生成AIなどを使えばテストコード作成の負担も減らせるし。
そんなわけで、私も自身が開発しているWordPressプラグインにPHPUnitによるテストができるようにしてみようと調べ始めたのだけど、情報が意外と少なく、テスト環境構築に苦労したのでここに備忘録として記すことにした。
環境
- MacOS 15
- MAMP 7.1
- PHP 8.2.20 (Apache) / 8.4.3 (cli)
- MySQL 8.0.35
事前準備
- WP CLIのインストール
- mysqlやmysqladminへのパスを通しておく
- svnのインストール
MySQLへのパスを通す
MAMPのMySQLは/Applications/MAMP/Library/bin/mysql80/bin
に存在する(バージョン8系の場合)。~/.zshrcに以下のように追記する。(シェルがzshの場合)
export PATH=/Applications/MAMP/Library/bin/mysql80/bin:$PATH
私は当初、~/.bash_profileに追記しており、反映されないことに頭を悩ませていた。(~/.bash_profileはbashをシェルとして使用している場合)
サンプルプラグインのスカフォールドを作成
いきなり開発中のプラグインで試すのは怖いのと、テストに必要なファイル類のサンプルを取得するためにサンプルプラグインのスカフォールドを作成する。
//WordPressインストールディレクトリへ移動
cd wptest
// sample-pluginという名前でプラグインのスカフォールドを作成
wp scaffold plugin sample-plugin
このようなファイル群が作成される。
sample-plugin % tree
.
├── Gruntfile.js
├── bin
│ └── install-wp-tests.sh
├── package.json
├── phpunit.xml.dist
├── readme.txt
├── sample-plugin.php
└── tests
├── bootstrap.php
└── test-sample.php
3 directories, 8 files
テスト用WordPressの作成
通常使用しているWordPressとは別に、テスト環境用のWordPressを作成する必要がある。そのためのスクリプトが./bin/install-wp-tests.shなので、これにDB接続用パラメーターを付加して実行する。
//./bin/install-wp-tests.sh {DB名} {user} {pass} {host} {version} {DB作成をスキップするか}
./bin/install-wp-tests.sh wordpress_test root root 127.0.0.1:8889 latest false
sample-plugin % ./bin/install-wp-tests.sh wordpress_test root root 127.0.0.1:8889 latest false
+ install_wp
+ '[' -d /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress ']'
+ return
+ install_test_suite
++ uname -s
+ [[ Darwin == \D\a\r\w\i\n ]]
+ local ioption=-i.bak
+ '[' '!' -d /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib ']'
+ '[' '!' -f wp-tests-config.php ']'
+ download https://develop.svn.wordpress.org/tags/6.7.1/wp-tests-config-sample.php /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib/wp-tests-config.php
++ which curl
+ '[' /usr/bin/curl ']'
+ curl -s https://develop.svn.wordpress.org/tags/6.7.1/wp-tests-config-sample.php
++ echo /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress
++ sed 's:/\+$::'
+ WP_CORE_DIR=/var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress
+ sed -i.bak 's:dirname( __FILE__ ) . '\''/src/'\'':'\''/var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress/'\'':' /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib/wp-tests-config.php
+ sed -i.bak 's:__DIR__ . '\''/src/'\'':'\''/var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress/'\'':' /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib/wp-tests-config.php
+ sed -i.bak s/youremptytestdbnamehere/wordpress_test/ /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib/wp-tests-config.php
+ sed -i.bak s/yourusernamehere/root/ /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib/wp-tests-config.php
+ sed -i.bak s/yourpasswordhere/root/ /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib/wp-tests-config.php
+ sed -i.bak 's|localhost|127.0.0.1:8889|' /var/folders/ld/bn6sygf564z8l24hp00b_l8r0000gn/T/wordpress-tests-lib/wp-tests-config.php
+ install_db
+ '[' false = true ']'
+ PARTS=(${DB_HOST//\:/ })
+ local PARTS
+ local DB_HOSTNAME=127.0.0.1
+ local DB_SOCK_OR_PORT=8889
+ local EXTRA=
+ '[' -z 127.0.0.1 ']'
++ echo 8889
++ grep -e '^[0-9]\{1,\}$'
+ '[' 8889 ']'
+ EXTRA=' --host=127.0.0.1 --port=8889 --protocol=tcp'
++ mysql --user=root --password=root --host=127.0.0.1 --port=8889 --protocol=tcp '--execute=show databases;'
++ grep '^wordpress_test$'
mysql: [Warning] Using a password on the command line interface can be insecure.
+ '[' wordpress_test ']'
+ echo 'Reinstalling will delete the existing test database (wordpress_test)'
Reinstalling will delete the existing test database (wordpress_test)
+ read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB
Are you sure you want to proceed? [y/N]: y
+ recreate_db y
+ shopt -s nocasematch
+ [[ y =~ ^(y|yes)$ ]]
+ mysqladmin drop wordpress_test -f --user=root --password=root --host=127.0.0.1 --port=8889 --protocol=tcp
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
Database "wordpress_test" dropped
+ create_db
+ mysqladmin create wordpress_test --user=root --password=root --host=127.0.0.1 --port=8889 --protocol=tcp
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
+ echo 'Recreated the database (wordpress_test).'
Recreated the database (wordpress_test).
+ shopt -u nocasematch
すでにデータベースが存在する場合は削除するか聞かれるのでyで続行する。
svnがインストールされていなかったり、mysqlやmysqladminへのパスが通っていないと失敗する。その場合は原因を解消してから、wordpress-tests-libディレクトリを手動で削除し、再度実行する。
PHPUnitのインストール
composerでPHPUnitをプラグインの依存関係としてインストールする。この時のポイントがPHPUnitのバージョン。PHPやWordPressに合ったものをインストールする必要がある。今回は9.0を指定してインストールする。
composer require --dev phpunit/phpunit "^9.0"
No composer.json in current directory, do you want to use the one at /Applications/MAMP/htdocs/wptest? [Y,n]? n
./composer.json has been created
Running composer update phpunit/phpunit
Loading composer repositories with package information
composer.jsonが無い場合、親フォルダのを使用するか聞かれるのでnで、カレントディレクトリに作成する。
polyfillsのインストール
composer require --dev yoast/phpunit-polyfills "^3.1"
autoloadの設定
composer.jsonにオートロード設定を追加
{
"require-dev": {
"phpunit/phpunit": "^9.0",
"yoast/phpunit-polyfills": "^3.1"
},
"autoload-dev": {
"classmap": [
"tests"
]
}
}
phpunit.xmlの作成
phpunit.xml.distをphpunit.xmlにリネームし、サンプルテストを除外している部分をコメントアウトする。
<?xml version="1.0"?>
<phpunit
bootstrap="tests/bootstrap.php"
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<testsuites>
<testsuite name="testing">
<directory prefix="test-" suffix=".php">./tests/</directory>
<!--<exclude>./tests/test-sample.php</exclude>-->
</testsuite>
</testsuites>
</phpunit>
テスト実行
これで準備ができたのでテストを実行してみる。
./vendor/bin/phpunit
Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
Not running ajax tests. To execute these, use --group ajax.
Not running ms-files tests. To execute these, use --group ms-files.
Not running external-http tests. To execute these, use --group external-http.
PHPUnit 9.6.22 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 00:00.007, Memory: 38.50 MB
OK (1 test, 1 assertion)
このように、「OK (1 test, 1 assertion)」と表示されれば成功。
WordPressの機能を使ったテストもしてみる。(テストコードはClaudeに作成してもらった)
test-wp-functions.phpをtestsディレクトリへ作成する。
<?php
/**
* Class WP_Functions_Test
*
* @package Sample_Plugin
*/
/**
* WordPress functions test cases.
*/
class WP_Functions_Test extends WP_UnitTestCase {
/**
* 投稿の作成と取得をテスト
*/
public function test_create_and_get_post() {
$post_data = array(
'post_title' => 'テスト投稿',
'post_content' => 'これはテスト投稿の本文です。',
'post_status' => 'publish',
'post_author' => 1
);
// 投稿を作成
$post_id = wp_insert_post($post_data);
// 投稿が正常に作成されたか確認
$this->assertNotEquals(0, $post_id);
$this->assertGreaterThan(0, $post_id);
// 作成した投稿を取得
$post = get_post($post_id);
// 投稿の内容を検証
$this->assertEquals('テスト投稿', $post->post_title);
$this->assertEquals('これはテスト投稿の本文です。', $post->post_content);
}
/**
* カスタムフィールド(メタデータ)のテスト
*/
public function test_post_meta() {
// テスト用の投稿を作成
$post_id = $this->factory->post->create();
// メタデータを追加
$meta_key = 'test_meta_key';
$meta_value = 'テストメタ値';
add_post_meta($post_id, $meta_key, $meta_value);
// メタデータを取得して検証
$saved_value = get_post_meta($post_id, $meta_key, true);
$this->assertEquals($meta_value, $saved_value);
// メタデータを更新
$new_value = '新しいメタ値';
update_post_meta($post_id, $meta_key, $new_value);
// 更新されたメタデータを検証
$updated_value = get_post_meta($post_id, $meta_key, true);
$this->assertEquals($new_value, $updated_value);
}
/**
* カスタム投稿タイプの登録と検証をテスト
*/
public function test_custom_post_type() {
// カスタム投稿タイプを登録
register_post_type('test_cpt', array(
'public' => true,
'label' => 'テストCPT'
));
// カスタム投稿タイプが正しく登録されているか確認
$post_type_object = get_post_type_object('test_cpt');
$this->assertNotNull($post_type_object);
$this->assertEquals('テストCPT', $post_type_object->label);
// カスタム投稿タイプの投稿を作成
$post_id = $this->factory->post->create(array(
'post_type' => 'test_cpt'
));
// 作成した投稿を取得して検証
$post = get_post($post_id);
$this->assertEquals('test_cpt', $post->post_type);
}
/**
* タクソノミーとタームのテスト
*/
public function test_taxonomy_and_terms() {
// カスタムタクソノミーを登録
register_taxonomy('test_tax', 'post', array(
'hierarchical' => true,
'label' => 'テストタクソノミー'
));
// タームを作成
$term_data = wp_insert_term('テストターム', 'test_tax');
$this->assertNotWPError($term_data);
$term_id = $term_data['term_id'];
// 投稿を作成
$post_id = $this->factory->post->create();
// 投稿にタームを設定
wp_set_object_terms($post_id, $term_id, 'test_tax');
// 投稿に設定されたタームを取得して検証
$terms = wp_get_object_terms($post_id, 'test_tax');
$this->assertCount(1, $terms);
$this->assertEquals('テストターム', $terms[0]->name);
}
}
再度テストを実行してみて、テストの数が増えていれば成功。
./vendor/bin/phpunit
Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
Not running ajax tests. To execute these, use --group ajax.
Not running ms-files tests. To execute these, use --group ms-files.
Not running external-http tests. To execute these, use --group external-http.
PHPUnit 9.6.22 by Sebastian Bergmann and contributors.
..... 5 / 5 (100%)
Time: 00:00.045, Memory: 38.50 MB
OK (5 tests, 13 assertions)
Comments