Skip to main content
Version: 1.x

HTTP Tests

Introduction

Mantle provides a fluent HTTP Request interface to make it easier to write feature/integration tests using PHPUnit and WordPress. This library is a derivative work of Laravel's testing framework, customized to work with WordPress.

In short, this library allows one to mimic a request to WordPress, and it sets up WordPress' global state as if it were handling that request (e.g. sets up superglobals and other WordPress-specific globals, sets up and executes the main query, loads the appropriate template file, etc.). It then creates a new Test_Response object which stores the details of the HTTP response, including headers and body content. The response object allows the developer to make assertions against the observable response data, for instance asserting that some content is present in the response body or that some header was set.

For example, here's a simple test that asserts that a given post's title is present on the page:

namespace Tests\Feature;

use Tests\TestCase;

class Example_Test extends TestCase {
/**
* A basic test example.
*
* @return void
*/
public function test_basic_test() {
$post = static::factory()->post->create_and_get( [
'post_title' => 'Hello World',
] );

$this->get( $post->permalink() )
->assertOk()
->assertSee( 'Hello World' );
}
}

The get method makes a GET requests to the given URI, and the assertSee method asserts that the given string is present in the response body. All HTTP methods are available to use including get, post, put, patch, delete and options.

Making Requests

As a basic example, here we create a post via its factory, then request it and assert we see the post's name (title):

$post = static::factory()->post->create_and_get();

$this->get( $post )
->assertSee( $post->post_title );

In this example, here we request a non-existing resource and assert that it yields a 404 Not Found response:

$this->get( '/this/resource/does/not/exist/' )
->assertNotFound();

Lastly, here's an example of POSTing some data to an endpoint, and after following a redirect, asserting that it sees a success message:

$this->following_redirects()
->post( '/profile/', [ 'some_data' => 'hello' ] )
->assertSee( 'Success!' );

Request Cookies

Requests can have cookies included with them using the with_cookie and with_cookies methods:

$this
->with_cookie( 'session', 'cookie-value' );
->get( '/endpoint' );

// Pass multiple cookies.
$this
->with_cookies( [
'key' => 'value',
'another' => 'value',
] )
->get( '/example' );

Request Headers

Request headers can be set for requests using the with_header and with_headers methods:

$this
->with_header( 'api-key', '<value>' )
->get( '/example' );

$this
->with_headers( [
'API-Key' => '<value>',
'X-Nonce' => 'nonce',
] )
->get( '/example' );

Request Referrer

The request referrer can be passed using the from method:

$this->from( 'https://wordpress.org/' )->get( '/example' );

Asserting HTML Responses

HTML responses can be tested against using various methods to assert the response, including assertSee(), assertElementExists(), assertElementMissing(), assertQuerySelectorExists(), and assertQuerySelectorMissing().

The assertElementExists() and assertElementMissing() methods use DOMXPath to validate if a element exists on the page. The assertQuerySelectorExists() and assertQuerySelectorMissing() methods use the Symfony CSS Selector to validate if a element exists on the page.

$this->get( '/example' )
->assertOk()
->assertSee( 'mantle' )
->assertElementExists( '//script[@data-testid="example-script"]' )
->assertQuerySelectorExists( 'script[data-testid="example-script"]' );

You can also use other methods to assert if the response has/does not have an element by class or ID.

$this->get( '/example' )
->assertOk()

// By class name.
->assertElementExistsByClass( 'example-class' )
->assertElementMissingByClass( 'invalid-class' )

// By element ID.
->assertElementExistsById( 'example-id' )
->assertElementMissingById( 'invalid-id' )

// By tag name.
->assertElementExistsByTagName( 'div' )
->assertElementMissingByTagName( 'script' );

For more information see Element Assertions.

Query Assertions

Mantle supports making assertions against the global WordPress query and its related queried object/ID.

You may use assertQueryTrue() to assert the given WP_Query is_ functions (is_single(), is_archive(), etc.) return true and all others return false.

$this->get( static::factory()->post->create_and_get() )
->assertQueryTrue( 'is_single', 'is_singular' );

$this->get( '/' )
->assertQueryTrue( 'is_home', 'is_front_page );

You may use assertQueriedObjectId() to assert the given ID matches the result of get_queried_object_id(). assertQueriedObject() can be used to assert that the type and ID of the given object match that of get_queried_object().

$this->get( static::factory()->post->create_and_get() )
->assertQueriedObjectId( $post->ID )
->assertQueriedObject( $post );

Debugging Responses

After making a test request, you may use the dump or dump_headers methods to dump the response body or headers to the console.

$this->get( '/example' )
->dump()
->dump_headers();

Alternatively, you may use the dd or dd_headers methods to print the response body or headers and end the test.

$this->get( '/example' )->dd();

$this->get( '/example' )->dd_headers();

You can use the dumpJson()/ddJson() methods to dump the JSON response body from a (optional) specific path.

$this->get( '/example' )
->dumpJson()
->dumpJson( 'data' );

$this->get( '/example' )->ddJson();

Testing JSON APIs

Mantle includes several helpers for testing JSON APIs and their responses. For example, assertJsonPath() can be used to easily check the response back from a JSON API. This supports both custom routes and the WordPress REST API. JSON-requests can also be made via helper functions json(), get_json(), post_json(), and put_json().

The assertJsonPath() supports passing in a XPath to compare a specific element inside a JSON response. In the example below, we'll be retrieving the id element from the JSON object returned and comparing the value against an expected value.

$post_id = static::factory()->post->create();

$this->get( rest_url( "/wp/v2/posts/{$post_id}" ) )
->assertJsonPath( 'id', $post_id );

For more information see JSON Assertions.

Asserting Exact JSON Matches

As previously mentioned, the assertJsonPath() method may be used to assert that a specific JSON. There are also times when you wish to match the JSON response exactly. Using assertJsonMissing() Mantle will compare the response back and assert if the JSON returned matches the expected value.

$this->get( rest_url( '/mantle/v1/example' ) )
->assertJsonMissing( [
'key' => 'value',
// ...
] );

assertJsonMissingExact() can also be used to assert that the response does not contain the exact JSON fragment.

$this->get( rest_url( '/mantle/v1/example' ) )
->assertOk()
->assertJsonMissingExact( [
'invalid' => 'value',
] );

Before/After Callbacks

You may use before_request() and after_request() to register callbacks to be run before and after each test request. These callbacks are useful for registering and unregistering hooks, filters, and other WordPress functionality that may be required for your tests.

namespace App\Tests;

class Example_Callback_Test extends Test_Case {
protected function setUp(): void {
parent::setUp();

$this->before_request( function() {
// Register hooks, filters, etc. that should apply to HTTP requests.
} );

$this->after_request( function() {
// Unregister hooks, filters, etc.
} );
}

public function test_example_callbacks() {
// ...
}
}

Available Assertions

Mantle\Testing\Test_Response provides many assertions to confirm aspects of the response return as expected.

HTTP Status Assertions

assertSuccessful

Assert that a response has a (>= 200 and < 300) HTTP status code.

$response->assertSuccessful();

assertOk

Assert that a response has a 200 HTTP status code.

$response->assertOk();

assertStatus

Assert that a response has a given HTTP status code.

$response->assertStatus( $status );

assertCreated

Assert that a response has a 201 HTTP status code.

$response->assertCreated();

assertNoContent

Assert that a response has a given status code (default to 204) and no content.

$response->assertNoContent( $status = 204 );

assertNotFound

Assert that a response has a 404 HTTP status code.

$response->assertNotFound();

assertForbidden

Assert that a response has a 403 HTTP status code.

$response->assertForbidden();

assertUnauthorized

Assert that a response has a 401 HTTP status code.

$response->assertUnauthorized();

assertRedirect

Assert that a response has a redirect to a given URI and has a 301 or 302 HTTP status code.

$response->assertRedirect( $uri = null );

Element Assertions

Element assertions are used to assert the presence, absence, etc. of elements in the response body. These assertions use DOMXPath and support query selectors via the symfony/css-selector package.

Are you looking to assert against a HTML string that is not a response?

If you are looking to assert against a HTML string that is not a response, you can use the HTML String helper to make assertions.

assertQuerySelectorExists

Assert that a given CSS selector exists in the response.

$response->assertQuerySelectorExists( string $selector );

assertQuerySelectorMissing

Assert that a given CSS selector does not exist in the response.

$response->assertQuerySelectorMissing( string $selector );

assertElementExists

Assert that a given XPath exists in the response.

$response->assertElementExists( string $expression );

assertElementMissing

Assert that a given XPath does not exist in the response.

$response->assertElementMissing( string $expression );

assertElementExistsByClass

Assert that a given class exists in the response.

$response->assertElementExistsByClass( string $class );

assertElementMissingByClass

Assert that a given class does not exist in the response.

$response->assertElementMissingByClass( string $class );

assertElementExistsById

Assert that a given ID exists in the response.

$response->assertElementExistsById( string $id );

assertElementMissingById

Assert that a given ID does not exist in the response.

$response->assertElementMissingById( string $id );

assertElementExistsByTagName

Assert that a given tag name exists in the response.

$response->assertElementExistsByTagName( string $tag_name );

assertElementMissingByTagName

Assert that a given tag name does not exist in the response.

$response->assertElementMissingByTagName( string $tag_name );

assertElementCount

Assert that the response has the expected number of elements matching the given XPath expression.

$response->assertElementCount( string $expression, int $expected );

assertQuerySelectorCount

Assert that the response has the expected number of elements matching the given CSS selector.

$response->assertQuerySelectorCount( string $selector, int $expected );

assertElementExistsByTestId

Assert that an element with the given data-testid attribute exists in the response.

$response->assertElementExistsByTestId( string $test_id );

assertElementMissingByTestId

Assert that an element with the given data-testid attribute does not exist in the response.

$response->assertElementMissingByTestId( string $test_id );

assertElement

Assert that the given element exists in the response and passes the given assertion. This can be used to make custom assertions against the element that cannot be expressed in a simple XPath expression or query selector.

$response->assertElement( string $expression, callable $assertion, bool $pass_any = false );

If $pass_any is true, the assertion will pass if any of the elements pass the assertion. Otherwise, all elements must pass the assertion. Let's take a look at an example:

use DOMElement;

$response->assertElement(
'//div',
fn ( DOMElement $element ) => $this->assertEquals( 'Hello World', $element->textContent )
&& $this->assertNotEmpty( $element->getAttribute( 'class' ) ) );
},
);

assertQuerySelector

Assert that the given CSS selector exists in the response and passes the given assertion. Similar to assertElement, this can be used to make custom assertions against the element that cannot be expressed in a simple XPath expression or query selector.

$response->assertQuerySelector( string $selector, callable $assertion, bool $pass_any = false );

Let's take a look at an example:

use DOMElement;

$response->assertQuerySelector(
'div > p',
fn ( DOMElement $element ) => $this->assertEquals( 'Hello World', $element->textContent )
&& $this->assertNotEmpty( $element->getAttribute( 'class' ) ) );
},
);

Header Assertions

assertLocation

Assert that the response has a Location header matching the given URI.

$response->assertLocation( $uri );

assertHeader

Assert that the response has a given header and optionally the given value.

$response->assertHeader( $header_name, $value = null );

assertHeaderMissing

Assert that the response does not have a given header and optional value.

$response->assertHeaderMissing( $header_name );
$response->assertHeaderMissing( $header_name, $value );

JSON Assertions

assertJsonPath

Assert that the expected value and type exists at the given path in the response.

$response->assertJsonPath( string $path, $expect );

assertJsonPathExists

Assert that a specific JSON path exists.

$response->assertJsonPathExists( string $path );

assertJsonPathMissing

Assert that a specific JSON path is missing.

$response->assertJsonPathMissing( string $path );

assertExactJson

Assert that the response has the exact given JSON.

$response->assertExactJson( array $data );

assertJsonFragment

Assert that the response contains the given JSON fragment.

$response->assertJsonFragment( array $data);

assertJsonMissing

Assert that the response does not contain the given JSON fragment.

$response->assertJsonMissing( array $data );

assertJsonMissingExact

Assert that the response does not contain the exact JSON fragment.

$response->assertJsonMissingExact( array $data );

assertJsonCount

Assert that the response JSON has the expected count of items at the given key.

$response->assertJsonCount( int $count, string $key = null );

assertJsonStructure

Assert that the response has a given JSON structure.

$response->assertJsonStructure( array $structure = null);

assertIsJson

Assert that the response has a valid JSON structure and content-type header with application/json.

$response->assertIsJson();

assertIsNotJson

Assert that the response does not have a valid JSON structure or content-type header with application/json.

$response->assertIsNotJson();

Content Body Assertions

assertSee

Assert the given string exists in the body content

$response->assertSee( $value );

assertSeeInOrder

Assert the given strings exist in the body content in the given order

$response->assertSeeInOrder( array $values );

assertSeeText

Similar to assertSee() but strips all HTML tags first

$response->assertSeeText( $value );

assertSeeTextInOrder

Similar to assertSeeInOrder() but strips all HTML tags first

$response->assertSeeTextInOrder( array $values );

assertDontSee

Assert the given string does not exist in the body content.

$response->assertDontSee( $value );

assertDontSeeText

Similar to assertDontSee() but strips all HTML tags first.

$response->assertDontSeeText( $value );

WordPress Query Assertions

assertQueryTrue

Assert the given WP_Query's is_ functions (is_single(), is_archive(), etc.) return true and all others return false

$response->assertQueryTrue( ...$prop );

assertQueriedObjectId

Assert the given ID matches the result of get_queried_object_id().

$response->assertQueriedObjectId( int $id );

assertQueriedObject

Assert that the type and ID of the given object match that of get_queried_object().

$response->assertQueriedObject( $object );