$npx -y skills add mralaminahamed/wp-dev-skills --skill wp-plugin-testingUse when writing or setting up tests for a WordPress plugin — PHPUnit integration tests using WP_UnitTestCase with the WP test suite (bin/install-wp-tests.sh, phpunit.xml.dist, tests/bootstrap.php), unit tests with Brain Monkey (when() / expect() / Mockery) or WP_Mock, test facto
| 1 | # WordPress Plugin Testing |
| 2 | |
| 3 | > **Model note:** Bootstrap and CI config setup are mechanical (`haiku`). Writing meaningful test cases (choosing fixtures, mocking the right layer, testing edge cases) requires understanding the plugin's code — use `sonnet`. Redirect/exit harness setup is a one-time pattern and works on `haiku`. |
| 4 | |
| 5 | Set up and write automated tests for WordPress plugins: PHPUnit integration tests (real WP + DB), pure unit tests (no WP loaded), and acceptance/E2E tests with Codeception. |
| 6 | |
| 7 | ## When to use |
| 8 | |
| 9 | - "Set up PHPUnit tests for my plugin", "bootstrap a WP test suite", "scaffold plugin tests". |
| 10 | - "Write a unit test for this function", "mock WordPress functions without loading WP". |
| 11 | - "Set up Codeception / wp-browser", "write acceptance tests". |
| 12 | - "Test a hook callback", "assert a filter changes the output", "test AJAX handlers". |
| 13 | - "Add tests to CI", "run tests on GitHub Actions". |
| 14 | |
| 15 | **Not for:** PHPStan static analysis — use the official `wp-phpstan` skill. Scaffolding a stubs package for a third-party library — use `wp-phpstan-stubs`. Debugging CI failures on an existing suite — use `wp-ci-qa`. |
| 16 | |
| 17 | ## Method |
| 18 | |
| 19 | ### 1. Choose test type |
| 20 | |
| 21 | | Type | Tool | WP loaded | DB | Speed | |
| 22 | |---|---|---|---|---| |
| 23 | | Integration | PHPUnit + WP test suite (`WP_UnitTestCase`) | ✅ Full | ✅ Real (temp) | Slow | |
| 24 | | Unit | PHPUnit + Brain\Monkey (recommended) or WP_Mock | ❌ | ❌ | Fast | |
| 25 | | Acceptance | Codeception + wp-browser | ✅ Browser | ✅ Real | Slowest | |
| 26 | |
| 27 | Start with integration tests for hooks/filters; unit tests for pure business logic; acceptance only for critical user flows. |
| 28 | |
| 29 | ### 2. Integration test setup (WP test suite) |
| 30 | |
| 31 | **Install test suite:** |
| 32 | ```bash |
| 33 | # WP-CLI method (recommended) |
| 34 | wp scaffold plugin-tests my-plugin |
| 35 | |
| 36 | # Manual — install WP test library |
| 37 | bash bin/install-wp-tests.sh wordpress_test root '' localhost latest |
| 38 | ``` |
| 39 | |
| 40 | `bin/install-wp-tests.sh` creates a temp WP installation and the `wordpress-tests-lib`. Add `tests/` dir to `.gitignore` if downloading WP inline, or commit the bootstrap only. |
| 41 | |
| 42 | **`composer.json` additions:** |
| 43 | ```json |
| 44 | { |
| 45 | "require-dev": { |
| 46 | "phpunit/phpunit": "^9.0 || ^10.0", |
| 47 | "yoast/phpunit-polyfills": "^2.0" |
| 48 | }, |
| 49 | "scripts": { |
| 50 | "test": "phpunit", |
| 51 | "test:unit": "phpunit --testsuite=unit", |
| 52 | "test:integration": "phpunit --testsuite=integration" |
| 53 | } |
| 54 | } |
| 55 | ``` |
| 56 | |
| 57 | **`phpunit.xml.dist`:** |
| 58 | ```xml |
| 59 | <?xml version="1.0"?> |
| 60 | <phpunit bootstrap="tests/bootstrap.php" colors="true"> |
| 61 | <testsuites> |
| 62 | <testsuite name="integration"> |
| 63 | <directory>tests/integration</directory> |
| 64 | </testsuite> |
| 65 | <testsuite name="unit"> |
| 66 | <directory>tests/unit</directory> |
| 67 | </testsuite> |
| 68 | </testsuites> |
| 69 | </phpunit> |
| 70 | ``` |
| 71 | |
| 72 | **`tests/bootstrap.php` (integration):** |
| 73 | ```php |
| 74 | <?php |
| 75 | $_tests_dir = getenv( 'WP_TESTS_DIR' ) ?: '/tmp/wordpress-tests-lib'; |
| 76 | require_once $_tests_dir . '/includes/functions.php'; |
| 77 | |
| 78 | tests_add_filter( 'muplugins_loaded', function() { |
| 79 | require dirname( __DIR__ ) . '/my-plugin.php'; |
| 80 | } ); |
| 81 | |
| 82 | require $_tests_dir . '/includes/bootstrap.php'; |
| 83 | ``` |
| 84 | |
| 85 | ### 3. Write integration tests |
| 86 | |
| 87 | Extend `WP_UnitTestCase` (provided by WP test suite). It wraps each test in a DB transaction and rolls back — no teardown needed for posts/users/terms. |
| 88 | |
| 89 | ```php |
| 90 | class Test_My_Feature extends WP_UnitTestCase { |
| 91 | |
| 92 | public function test_filter_changes_title() { |
| 93 | $post_id = self::factory()->post->create( [ 'post_title' => 'Original' ] ); |
| 94 | |
| 95 | // Activate the plugin feature |
| 96 | add_filter( 'the_title', 'my_plugin_modify_title', 10, 2 ); |
| 97 | |
| 98 | $title = get_the_title( $post_id |