Skip to content

Application Testing with PHPUnit – WordPress Plugin Boilerplate

Demo Plugin runs PHPUnit in the isolated .wp-env.test.json environment. npm run test:php starts it automatically.

The container provides a real WordPress database and the full WordPress function library required by the WordPress test bootstrap. The bootstrap lives in tests/bootstrap.php, configuration in phpunit.xml.dist, and test classes go in tests/php.

  1. Add a test class in tests/php. An ExampleTest.php is already provided as a starting point.

    tests/php/MyFeatureTest.php
    <?php
    class MyFeatureTest extends WP_UnitTestCase {
    public function test_something(): void {
    $this->assertTrue( true );
    }
    }
  2. Run tests.

    Terminal
    npm run test:php
tests/php/PostMetaBehaviorTest.php
<?php
class PostMetaBehaviorTest extends WP_UnitTestCase {
public function test_can_store_and_read_post_meta(): void {
$post_id = self::factory()->post->create();
update_post_meta( $post_id, '_demo_flag', 'yes' );
$this->assertSame( 'yes', get_post_meta( $post_id, '_demo_flag', true ) );
}
}
tests/php/FilterRegistrationTest.php
<?php
class FilterRegistrationTest extends WP_UnitTestCase {
public function test_demo_filter_modifies_value(): void {
add_filter(
'demo_plugin/example_value',
static fn( string $value ): string => $value . '-modified'
);
$result = apply_filters( 'demo_plugin/example_value', 'before' );
$this->assertSame( 'before-modified', $result );
}
}

Some tests require other plugins to be present and active in the test environment.

Use WP-CLI inside the isolated test environment. The plugin slug matches its wordpress.org/plugins slug:

Terminal window
npm run env:test:cli -- wp plugin install advanced-custom-fields --version=6.3.0 --activate

Private and premium plugins are managed through a dedicated tests/composer.json. Composer installs them into tests/setup/plugins/, which is mapped into the wp-env WordPress installation before the environment starts.

Run npm run env:test:cli -- composer update --working-dir=tests to update test dependencies.

  1. Add the private Composer repository and package to tests/composer.json. Use an explicit installer path keyed to the package name so the install directory is predictable.

    tests/composer.json
    {
    "repositories": [
    {
    "type": "composer",
    "url": "https://composer.my-premium-plugin.com"
    }
    ],
    "require": {
    "vendor/my-premium-plugin": "*"
    },
    "config": {
    "allow-plugins": {
    "composer/installers": true
    }
    }
    }
  2. Create tests/auth.json locally with credentials for the private repository.

    tests/auth.json
    {
    "http-basic": {
    "composer.my-premium-plugin.com": {
    "username": "your-license-email",
    "password": "your-license-key"
    }
    }
    }
  3. Run the tests.

    Terminal
    npm run test:php
  4. For CI, add a COMPOSER_AUTH secret in GitHub Actions with the same JSON content as auth.json. The test-application workflow writes it to tests/auth.json before the tests run.

    GitHub Actions secret: COMPOSER_AUTH
    {
    "http-basic": {
    "composer.my-premium-plugin.com": {
    "username": "your-license-email",
    "password": "your-license-key"
    }
    }
    }

Future REST or browser tests can use getWpEnvUrls() from tests/helpers/wp-env.mjs to resolve the URLs reported by WordPress.

Namespaces in test files — WP_UnitTestCase is a global class so test classes conventionally use no namespace. If you prefer a namespace, add a Tests\ autoload entry in composer.json.

Local phpunit.xml — phpunit.xml.dist is committed as the shared default. Create a local phpunit.xml to override settings for your environment (it is already listed in .gitignore).

The test-application job in .github/workflows/test-analyse.yml runs the full test suite on every push via wp-env.

  • PHP67%
  • JavaScript33%