Automated testing for your WordPress – Steve Grunwell at WCUS

Automated testing for your WordPress

Steve Grunwell is a Senior Software Engineer at Liquid Web who works on Managed WordPress platforms, specializing in WP and web app development. He says WordPress is a tightly-coupled system with a history of ideas, decisions and technical shifts that can mean consequences for even simpler tasks. However, you can ensure software is released regularly with low regression risk with automated testing.

Steve Grunwell WCUS 2019 - Plesk

Building WordPress plugins with tests can seem challenging, however there are tools to set up a test harness within an existing codebase with ease. In his WCUS talk, Steve talked about the fundamentals of automated testing, particularly in regards to WordPress. Plus, how to start testing plugins and themes using features from PHPUnit and the WordPress core testing framework. In order to finally build and release quality software.

About Automated Testing

Achieving continuous integration and delivery is the holy grail. We can start automated the entire process from writing code to production. Automated testing plays a vital part in letting us reduce time and chance of human error. It is easily reproducible and a gateway to CI/CD.

For WordPress automation, testing, staging, smart updates and more, check out our complete Plesk WordPress Toolkit.

Test Types

Unit Test – Tests the smallest possible unit of an app. It’s often a single function.

Integration Test – Takes all the unit tests and finds if they work together in the way we’re expecting.

E2E (end-to-end) – Tests the entire path through the organization.

automated testing pyramid - Steve Grunwell

They may cost more to test the higher up the pyramid you go but maybe they take even longer to run. You are after all in many cases making HTTP requests.

SUT (System Under Test)

This refers to the current system we’re trying to test. It can be a single method, a class, or a whole feature. What are we trying to accomplish with our test? And how do we get everything else out of the way so we can focus on that?

When it comes to WP, we do have to shift a little. As we said, it’s a very tightly-coupled system. So, it’s very hard to test single items in true isolation. But this doesn’t mean we can’t do this effectively. And this is what Steve talked about at WCUS 2019.

PHPUnit – Our testing toolbox

Steve talked about PHPUnit by first explaining its structure.

Test Suite – This is a collection of test classes.

Test Class– a collection of one or more test cases.

Test Case – A single scenario you’re going to test.

It’s going to be comprised of one or more assertions. Do things work the way that we expect? Here are a few scenarios that Steve Grunwell highlights.

Is it true or false?

assertTrue () $value ===true?

$this->assertTrue(true);

assertFalse () $value ===false?

$this->assertFalse(false);

Equality

assertEquals()  $expected == $actual?

$this->assertEquals($expected, $actual);

assertSame()  $expected == $actual?

$this->assertSame($expected, $actual);

Verifying contents of things

assertContains () Does $value contain $expected?

$this->assertContains('b', ['a', 'b', 'c']);

assertRegexp() Does $value match the given $regex?

$this->assertRegexp('/^Fo+/', 'Foo Bar');

Negative assertions

For every assertion, there is a positive and negative assertion.

assertEquals () $expected ==$actual?

assertNotEquals () $expected ==$actual?

 

assertContains () $expected ==$actual?

assertNotContains () $expected ==$actual?

 

assertCount () $expected ==$actual?

assertNotCount () $expected ==$actual?

 

assertArrayHasKey () $expected ==$actual?

assertNotArrayHasKey () $expected ==$actual?

 

Do we have at least one match? Everything comes down to true or false. The key to understanding assertions in our tests. Here is an example of a test report:

PHPUnit 7.5.1 by Sebastian Bergmann and contributors.

...............................................  47 / 511 ( 9%)

...............................................  94 / 511 ( 18%)

...................................SSSS........ 141 / 511 ( 27%)

............................................... 188 / 511 ( 36%)

............................................... 235 / 511 ( 45%)

............................................... 282 / 511 ( 55%)

............................................... 329 / 511 ( 64%)

............................................... 376 / 511 ( 73%)

............................................... 423 / 511 ( 82%)

............................................... 470 / 511 ( 91%)

.........................................       511 / 511 (100%)

 

Time: 1.13 minutes, Memory: 42.00MB

 

OK, but incomplete, skipped, or risky tests!

Tests: 511, Assertions: 1085, Skipped: 4.

 

PHPUnit 7.5.1 by Sebastian Bergmann and contributors.

 

.......F........                                 16/16 (100%)

 

Time: 7.15 seconds, Memory: 14.00MB

 

There was 1 failure:

 

1) Tests\CoffeeTest::test_get_good_coffee

Failed asserting that two strings are identical.

--- Expected

+++ Actual

@@ @@

-'great, well-balanced coffee'

+'Starbucks'

 

/my-plugin/tests/test-coffee.php:14

 

FAILURES!

Tests: 16, Assertions: 19, Failures: 1.

It ran through over 1K test in under a minute. If you were to do this manually it would take days instead of minutes. 

Test Doubles

As we test things, sometimes we want to get things out of the way in our code. This is where test doubles come into play. The general idea is to remove any variables in our code and give ourselves test versions to replace actual systems. Always returning known values and ensuring systems behave a certain way. When dealing with test doubles, a popular library for creating test doubles is Mockery.

public function test_handles_empty_order_list() {

    $api = Mockery::mock( Api::class )->makePartial();

    $api->shouldReceive( 'get_all_orders' )

        ->once()

        ->andReturn( [] );

    $this->assertEmpty( $api->get_recent_orders() );

}

There’s also the PHPUnit Markup assertions, powered by DOMDocument. Lets use DOMDocuments to make a DOM query.

function test_button_contains_active_state() {

    $output = some_function();

    $this->assertContainsSelector('.button.active', $output);

}

WP Core Test Suite

This is what WP core itself uses to ensure all the PHP in WP is behaving the way we expect it to. If we want to use the core test suite, you can run $ wp scaffold plugin-tests my-plugin to generate test scaffolding via WP-CLI. Get the test suite out of the box.

We want to make sure certain things happen before every test method. You don’t have to write it every time, only once.

We have the concept of groups where we run tests of a similar nature across suites and classes. I can just run the following code.

/**

 * @group Posts

 * @group PostMeta

 */

public function test_includes_private_posts()

{

    // ...

}

$ phpunit --group=Posts

This comes in handy when you have a large test suite and want to make sure related things aren’t going to break.

Data Providers

Often in our testing you can have the same test but different data. For this, we have a nice tool called data providers. You can run through them without having to paste the same method over and over again. So we specify a data provider for it. If you’re working with simple data types like strings and integers. You can choose to define just one method for example:

/**

 * @dataProvider my_data_provider()

 */

public function test_my_function( $expected, $value ) {

    $this->assertEquals( $expected, my_function( $value ) );

}

 

public function my_data_provider() {

    return [

        'Description of case 1' => ['foo', 'bar'],

        'Description of case 2' => ['bar', 'baz'],

    ];

}

/**

 * @testWith ["foo", "bar"]

 *           ["bar", "baz"]

 */

public function test_my_function( $expected, $value ) {

    $this->assertEquals( $expected, my_function( $value ) );

}

You can even generate dummy data with factories tests. You can generate users, posts and more – for testing purposes.

// Create the post and retrieve its ID.

$post_id = $this->factory->post->create();

 

// Create and retrieve the new post.

$post = $this->factory->post->create_and_get();

 

// Override default parameters.

$post = $this->factory->post->create_and_get( [

    'post_title'  => 'My Test Post',

    'post_author' => $author_id,

] );

 

// Create multiple instances.

$posts = $this->factory->post->create_many( 5, [

    'post_author' => $author_id,

] );

Checking for WP_ERRORS

Was the response an instance of WP_Error? Coming back to the search for truth – Is truth a WP_Error? As we write our code, there’s a pattern for how this should be arranged to set up the scenario.

public function test_function_can_return_wp_error() {

    $response = myplugin_function();

 

    $this->assertWPError($response);

}

Next we execute the code, and finally we make assertions around it – in other words, verify that things happened as you expected.

Testing Permissions

public function test_non_admins_cannot_clear_cache() {

    // Arrange

    $user_id = $this->factory->user->create( [

        'role' => 'author',

    ] );

 

    wp_set_current_user( $user_id );

 

    // Act

    $response = myplugin_clear_cache();

 

    // Assert

    $this->assertWPError($response);

    $this->assertSame(403, $response->get_error_code());

}

Registering a custom post type

public function test_book_cpt_is_registered() {

    myplugin_register_post_types();

 

    $post_type = get_post_type_object( 'book' );

 

    // Verify the post type is registered along with key properties.

    $this->assertNotNull( $post_type );

    $this->assertTrue( $post_type->public );

    $this->assertFalse( $post_type->hierarchical );

}

Testing Hooks

public function test_function_does_action() {

    myplugin_function();

 

    $this->assertSame( 1, did_action( 'myplugin_action' ) );

}

public function test_function_does_action() {

    $called = false;

 

    // Register a callback to validate arguments.

    add_action( 'myplugin_action', function () use (&$called) {

 

        // Only return true if validations passed.

        $called = true;

    } );

 

    myplugin_function();

 

    $this->assertTrue( $called );

}

Testing Output

public function test_shortcode_output() {

    ob_start();

    do_shortcode( '[recent-posts title="Latest Posts"]' );

    $output = ob_get_clean();

 

    $this->assertContains( '<h2>Latest Posts</h2>', $output );

}

public function test_shortcode_output() {

    $this->expectOutput( '<h2>Latest Posts</h2>' );

 

    do_shortcode( '[recent-posts title="Latest Posts"]' );

}

Stubbing HTTP Requests

add_filter( 'pre_http_request', function () {

    return [

        'headers'  => [],

        'body'     => '',

        'response' => [

            'code'    => 200,

            'message' => 'OK',

        ],

        'cookies'  => [],

        'filename' => '',

    ];

} );

Basic Automated Testing Workflow

Steve explains the basic idea behind TDD – test driven development.

  1. Write a (failing) test to describe the functionality/behavior. You’re describing how it should work. This can be called ‘red’ – there is a broken code.
  2. Write the code necessary to make the test pass. All we have to do is get the test to pass. This can be known as green – the code that works.
  3. Refactor, rinse, & repeat. Now we can go back and refine the code.

Automation is the way forward and one that strongly resonates with Plesk’s values and beliefs. You can find the slide deck from the talk here. Thanks Steve for sharing your expertise on automated testing!

Three TED talks on Technology that will blow your mind

We’re living in an era that reveals new innovations on the daily. From automation to complex and brilliant security systems, the future of technology is being shaped by minds like these three whose ideas elevate our minds and spark our imagination.

Watch our top TED Talks on technology

These three speakers have tested the boundaries of how we can integrate the physical world and the digital one. Here are three must-watch TED talks on technology that have mesmerized us and left us wondering what the future holds.

Will automation take away all our jobs? | David Autor

As a company focusing heavily on automation and simplicity as a time-saving solution, we found David Autor’s paradox intriguing. He says that in the last century, despite having created machines that to do our work for us, the proportion of adults in the US with a job has consistently gone up for the past 125 years.

So why hasn’t human work become redundant yet and how are our skills still not obsolete? In this talk about the future of work, economist David Autor addresses the question of why there are still so many jobs and comes up with a surprising answer. Do you agree with his theory?

Hackers: the internet’s immune system | Keren Elazari

Keren Elazari is a cybersecurity expert who claims that we actually need hackers in today’s day and age. Her shocking exclamation comes from her belief that hackers force us to evolve and improve. “They just might be the immune system for the information age”, she says.

Some hackers are fighting corruption and defending our rights. They also expose the loop holes and vulnerabilities in our systems and make us fix them.

But not all hackers use their superpowers for good. Would you take any chances with security loopholes? Let us know what you think about this video and learn more about Plesk security here.

 

Are you safe? Take the Plesk Security Quiz.

The mind behind Linux | Linus Torvalds

This is the guy who has transformed technology, not once, but twice. Linus Torvalds first gave us Linux kernel, which helps power the Internet, and then Git, the source code management system that developers use all over the world. This is more than a talk, but an interview where Torvalds discusses his personality traits which shaped his work philosophy and engineering. Plus, some useful open source tips for the developers watching.

“I am not a visionary, I’m an engineer,” Torvalds says. “I’m perfectly happy with all the people who are walking around and just staring at the clouds … but I’m looking at the ground, and I want to fix the pothole that’s right in front of me before I fall in.” Are you like Linus and do you agree with his philosophies?

Empowering you with TED talks on technology

As we got a glimpse of what these three researchers presented on stage, the common theme in all of the talks was making a better digital world together. Technology can empower people by educating them and giving them a voice, future designs succeed in bridging the two worlds together. A concept we at Plesk are on definitely board with.

Plesk Joomla! Toolkit brings automation and privacy enhancements to your Joomla! sites

With over 11 million websites running on Plesk, creating a toolkit for Joomla! was the natural next step for us. We wanted to bring a set of tools that enhanced Joomla! site performance, improves privacy and security, and is agile and versatile for all purposes.

Get Joomla! Toolkit

Specifically, we wanted to focus on two main problems that Joomla! users and agencies face: First, we wanted to address the challenge of managing and running multiple Joomla! Instances, for multiple clients. Second, we wanted to find a better way to guarantee privacy and security than passing data outside the server to an external Joomla! management application.

plesk-joomla-toolkit-detail

One of the biggest challenges freelancers and agencies face is having to waste valuable time to repeatedly execute tasks on multiple Joomla! instances. Tasks such as updating the Joomla! core are time-consuming, technically complex and highly risky when untested. Most importantly, the hours spent doing these tasks are usually neither strategic, innovative, nor billable. As a result, many Joomla! users typically opt not to run these updates unless absolutely necessary.

plesk-joomla-toolkit-overview

With the Joomla! Toolkit, we created a single entry point to gain a master view of all your Joomla! instances. You can easily scan and execute important security updates within the Plesk UI, without having to sign into every Joomla! backend.

joomla-tookit-security

By simplifying these daily maintenance processes, you will feel more compelled to keep all instances up-to-date and secure.

Our second goal for Joomla! Toolkit was to create a safe environment where privacy and security best practices are applied. To achieve that, we designed the toolkit to keep everything on the same server and allow commands to be executed locally, without the use of “connector scripts” to pass information to a management application on an external server. This innovation sets us firmly apart from similar solutions, and not only guarantees security and privacy for both users and their clients, but also offers a measurable performance advantage.

plesk-joomla-toolkit-extension-update

With these two core enhancements and many other features, we allow users and agencies who use Joomla! to save time, effort and money, so they can focus on the important things. But we’re not resting on our laurels, attending Joomla! Conferences to listen and get feedback from our existing and potential users about their needs, and what problems we could help to solve in their daily work lives. Based on these real-world inputs, we can continue to develop the Joomla! Toolkit into a valuable and meaningful application.

So where can you get the Plesk Joomla! Toolkit? Get a preview version of the toolkit from our launch partner Aruba.it, and watch out for the full-featured version that will launch in Spring 2018.