How To Start Testing Your WordPress Code With the Pest PHP Testing Framework

We can all agree that WordPress has come a long way since its beginning and that it grew into something a lot more than blogging software.

At its core, it’s still a content management system (CMS), but with over 59,000 plugins in the wordpress.org directory, you can customize it to be much more.

The reason for its popularity is its low entry barrier for both content creators and developers. Sometimes this comes with a cost. It’s no secret that WordPress has a bad reputation when it comes to development. It has a lot of legacy baggage and die-hard rules that prevent any backward compatibility breaking change when it comes to PHP code (Gutenberg is another story I won’t get into).

That legacy PHP code is often used by the developers that are starting to enter the world of programming, and the issue with that is they can learn some bad programming patterns. That in turn means they will reuse the poorly written code, increasing the amount of bad code in the world.

This is where WordPress gets its bad reputation in the developer community.

Breaking the cycle

So how can we break this cycle of bad code? By teaching new developers how they should write good code. One example of teaching new developers (but also old ones that are still clinging to the ‘WordPress’ way of doing things) is by writing tutorials.

Another way is to encourage them to use tools that can help them write better code.

I’m currently involved in the work which aims to release the new version of the WordPress Coding Standards, a set of rules used for the PHP_CodeSniffer tool that will let you know if your code has some potential issues (security, best practices, code style).

Another tool that I’ve recently developed is a package that will help developers set up WordPress integration tests that use the Pest testing framework.

Ok, so why do we need this new tool?

The main motivation behind creating this package is to encourage more people to write tests for their code, especially plugin developers.

A lot of developers in the WordPress community go with the mantra: I can see that it works because I’ve tried it out in my browser. That’s OK, but there are issues with that.

First, it’s time-consuming. Every time you make some change, you need to make sure it works, but also that you didn’t break anything.

Second, people make mistakes. We are not foolproof, and code may be misused in ways you never thought possible. You would be amazed at how creative people can be when writing code.

Automated tests are quick and can help you in testing various cases that will happen when you execute your code.

You test for the intended behavior (happy path), and in a quick way, you can add examples of how your code can be used in a way you didn’t intend it to be used (unhappy path).

It also safeguards your code from regressions. A code regression is when you unintentionally break one part of your code by adding new code.

The problem with tests set up so far

Testing in WordPress is not a new thing. And it’s not like you couldn’t set up tests for your code before. There are amazing libraries out there that will help you set everything up like wp-browser.

But the problem is that the setup procedure is often clunky.

You need to set up a separate database for tests, and you need to run certain scripts, then change files to make it all work.

Let’s face it, it’s not a straightforward thing to do, and developers are by nature lazy creatures (that’s why we write code to do things for us ).

The aim of the wp-pest integration test setup is to eliminate all that extra work.

How to set it up

In order to set it up, your project must use Composer. It’s a de-facto standard way of adding packages to your code.

In your terminal type

composer require dingo-d/wp-pest-integration-test-setup --dev

After you’ve downloaded the package and its dependencies you can set up the theme tests by typing

vendor/bin/wp-pest setup theme

Or, in the case you want to set up tests for your plugin, you can write

vendor/bin/wp-pest setup plugin --plugin-slug=your-plugin-slug

Optionally, you can provide a --wp-version parameter, to specify which WordPress version you’d like to test your code on.

In the background, a WordPress instance will be downloaded, and an in-memory database will be set up, along with two examples of tests that you can run.

Then, running either

vendor/bin/pest --group=unit

or

vendor/bin/pest --group=integration

will run the tests.

The beauty of Pest is that its syntax is developer-friendly. It has amazing documentation and great syntax. Let’s look at a simple example. Say you are registering a custom post type called ‘Books’:

<?php

/**
* Plugin Name: Test plugin
* Desctiption: Test plugin
* Version: 1.0.0
* License: MIT
*/

function test_plugin_register_books_cpt() {
$args = array(
'label' => esc_html__( 'Books', 'test-plugin' ),
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'book' ),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => null,
'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
);

register_post_type( 'book', $args );
}

add_action( 'init', 'test_plugin_register_books_cpt' );

After running the setup command that adds an example, a test called BooksCptTest.php would look like this:

<?php

namespace Tests\Integration;

beforeEach(function () {
parent::setUp();
});

afterEach(function () {
parent::tearDown();
});

test('Books custom post type is registered', function () {
// We can use assertions from PHP_Unit.
$this->assertNotFalse(has_action('init', 'test_plugin_register_books_cpt'));

$registeredPostTypes = \get_post_types();

// Or we can use expectations API from Pest.
expect($registeredPostTypes)
->toBeArray()
->toHaveKey('book');
});

Running vendor/bin/pest --group=integration gives us the following output:

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.

PASS Tests\\Integration\\BooksCptTest
✓ Books custom post type is registered

Tests: 1 passed
Time: 0.14s

Conclusion

And just like that, you have the ability to run WordPress integration tests in your theme or plugin. Tests are amazing because not only are they safeguarding us from mistakes, but they also force us to write clean and testable code. This is especially true for plugins that have complicated logic or are communicating with third-party APIs.

Writing tests for such a codebase will force you to think about what your code architecture looks like so you can easily write automated tests – not to mention the time and money you’ll save from not having to manually test everything.

If you think this is something you might benefit from, feel free to use it, and star the repository on GitHub.

Hopefully, this will encourage more WordPress developers to use tools that will enhance their coding skills.