Getting started with PHP tests

Local Development

If Starting from scratch

  1. Download a zip from the cno-starter-theme or start a new Github repo with that as the template.
  2. Migrate all files to a fresh Local WP site (including the .git folder if it exists)
  3. Follow the readme to get the theme started.

If starting on an existing repo

  1. Create a bin folder inside the theme folder and add 2 files: install-wp-tests.sh and local-mysql-socket.sh
  2. Copy the contents from the Github Template Theme repo
  3. Update composer.json to include the following key/value pairs:
    • autoload-dev
    • scripts.test and scripts.install-tests
    • Update the path to be your theme name (from cno-starter-theme to your-theme-name)
    • require-dev: add the missing packages (phpunit & yoast/phpunit-polyfills
  4. Run composer update -w to update/install new dependencies
  5. Add .wp-tests to your .gitignore
# .gitignore
# ...rest of .gitignore...

# Tests
.wp-tests/

Enabling Code Coverage

  1. In Local, click on Site Folder, then navigate to conf/php/php.ini.hbs.
  2. Open the file with TextEditor (VS Code autoformat can royally screw things up) and find the following:
# conf/php/php.ini.hbs

{{#if xdebugEnabled}}
xdebug.mode=debug,develop
  1. Add ‘coverage’ as the third item in the list
# conf/php/php.ini.hbs

{{#if xdebugEnabled}}
xdebug.mode=debug,develop,coverage
  1. Add the resulting new files to `.gitignore
# .gitignore 
# ...rest of .gitignore...

# Tests
.wp-tests/
.phpunit.result.cache
.phpunit.cache
html-coverage/

Init Test Suite Locally

  1. Click the Site Shell button on a local WP site
  2. Run wp scaffold theme-tests [theme-name]
    • When you see Warning: file already exists, skip the file
    • Delete / move the .circle-ci folder, .phpcs.xml.dist file, and phpunit.xml.dist (if you need the phpunit xml, copy it from the template repo and place it in the root of the repo (/public). Be sure to update the theme name if doing so!
  3. Run composer install-tests
  4. Run composer test

Integration Tests with ACF

Install the Test-Aware ACF (Pro) Plugin

If your tests require ACF fields (e.g. get_field), you’ll need to add some extra configuration to your composer.json and bootstrap files

  1. Choose either ACF or ACF Pro to install (go with ACF unless you need specific Pro functions)
  2. Add the following to your composer.json:
{
  // ...rest of json...
  "extra": {
	"installer-paths": {
      "vendor/{$name}/": [
		"wpengine/advanced-custom-fields"
	  ]
	}
  },
  "repositories": [
    {
      "type":"composer",
      "url":"https://composer.advancedcustomfields.com"
    }
  ],
  "require-dev": {
	// ...other required packages...
    "wpengine/advanced-custom-fields": "*"
  }
}
  1. Run composer update -w
  2. Update your theme/tests/bootstrap.php file with the following function:
tests_add_filter( 'muplugins_loaded', '_manually_load_acf' );
/**
 * Manually load the ACF plugin for testing.
 */
function _manually_load_acf() {
	if ( ! defined( 'WP_PLUGIN_DIR' ) ) {
		define( 'WP_PLUGIN_DIR', dirname( $_tests_dir, 1 ) . '/wordpress/wp-content/plugins' );
	}
	// Add ACF
	$acf_path = dirname( __DIR__, 4 ) . '/vendor/advanced-custom-fields';

	// turn off the ACF admin UI to speed tests and reduce noise
	if ( ! defined( 'ACF_LITE' ) ) {
		define( 'ACF_LITE', true );
	}

	if ( file_exists( $acf_path . '/acf.php' ) ) {
		require_once $acf_path . '/acf.php';
	} elseif ( file_exists( WP_PLUGIN_DIR . '/advanced-custom-fields/acf.php' ) ) {
		require_once WP_PLUGIN_DIR . '/advanced-custom-fields/acf.php';
	} else {
		// Optional: throw or log so CI fails loudly
		fwrite( STDERR, "ACF plugin not found at {$acf_path}/acf.php or " . WP_PLUGIN_DIR . "/advanced-custom-fields/acf.php\n" );
	}
}

Set up ACF within your test

According to ChatGPT, best practice is to create an isolated field group for what you’re testing. So, here’s an example for event post type fields

  1. in Class::set_up_before_class, call acf_add_local_field_group and register the fields you need for testing
  2. In Class::tear_down_after_class, call acf_remove_local_field_group and then call parent::tear_down_after_class()

You could also call this in set_up() and tear_down() depending on your test needs

Weirdness

  • PHPUnit must run version ^9 until things get updated (which you can track here)—even though PHPUnit is on v12 (at time of writing).
  • The final lines of install-wp-tests.sh symlink the WordPress tests directory so PHP intelephense knows about the test classes (no more red squigglies). You should see a .wp-tests/wordpress-tests-lib folder with /data, /includes, a wp-tests-config.php file and a wp-tests-config.php.bak file.
    • If you see these and see red squigglies, reload the window (VS Code Command Palette (Cmd + Shift + P) then "Developer: Reload Window")

See something inaccurate?