Skip to main content
Version: 1.x

Remote Requests

Introduction

Remote request mocks are a very common use case to test against when unit testing. Mantle gives you the ability to mock specific requests and fluently generate a response.

By default Mantle won't mock any HTTP request but will actively notify you when one is being made inside a unit test. To prevent any non-mocked requests from being made, see preventing stray requests.

note

This only supports requests made via the WP_Http API (wp_remote_request(), wp_remote_get(), wp_remote_post(), etc.)

Faking Requests

Intercept all remote requests with a specified response code and body using the $this->fake_request() method.

namespace App\Tests;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request()
->with_response_code( 404 )
->with_body( 'test body' );

// You can now make a remote request and it will return a 404.
$response = wp_remote_get( 'https://example.com/' );
}
}

The $this->fake_request() method returns a Mantle\Testing\Mock_Http_Response object that can allow you to fluently build a response. See generating responses for more information about building a response.

Faking a Specific Endpoint

You can specify a specific endpoint to fake by passing a URL to the $this->fake_request() method.

namespace App\Tests;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request( 'https://example.com/path' )
->with_response_code( 404 )
->with_body( 'test body' );

// You can now make a remote request to `https://example.com/path` and it will
// return a 404.
$response = wp_remote_get( 'https://example.com/path' );
}
}

You can also use wildcards in the URL to match multiple endpoints. The following example will match any request to https://example.com/*.

namespace App\Tests;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request( 'https://example.com/*' )
->with_response_code( 404 )
->with_body( 'test body' );

// You can now make a remote request to `https://example.com/` and it will
// return a 404.
$response = wp_remote_get( 'https://example.com/test' );
}
}

Faking Multiple Endpoints

Faking a specific endpoint, testing.com will return a 404 while github.com will return a 500.

namespace App\Tests;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request( 'https://testing.com/*' )
->with_response_code( 404 )
->with_body( 'test body' );

$this->fake_request( 'https://github.com/*' )
->with_response_code( 500 )
->with_body( 'fake body' );
}
}

Depending on your preferred style, you can also pass an array of URLs and responses to be faked using the mock_http_response() helper:

namespace App\Tests;

use function Mantle\Testing\mock_http_response;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request(
[
'https://github.com/*' => mock_http_response()->with_body( 'github' ),
'https://twitter.com/*' => mock_http_response()->with_body( 'twitter' ),
]
);
}
}

Faking With a Callback

If you require more complicated logic to determine the response, you can use a closure that will be invoked when a request is being faked. It should return a mocked HTTP response:

namespace App\Tests;

use function Mantle\Testing\mock_http_response;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request(
function( string $url, array $request_args ) {
if ( false === strpos( $url, 'alley.co' ) ) {
return;
}

return $this->mock_response()
->with_response_code( 123 )
->with_body( 'alley!' );
}
);
}
}

Faking Response Sequences

Sometimes a single URL should return a series of fake responses in a specific order. This can be accomplished via Mantle\Testing\Mock_Http_Sequence class and mock_http_sequence helper to build the response sequence.

In the following example, the first request to github.com will return a 200, the second a 400, and the third a 500:

namespace App\Tests;

use function Mantle\Testing\mock_http_sequence;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request(
[
'https://github.com/*' => mock_http_sequence()
// Push a status code.
->push_status( 200 )

// Push a JSON response.
->push_json( [ 1, 2, 3 ] )

// Push a response with a body.
->push_body( 'test body' )

// Push a entire response object.
->push( mock_http_response()->with_status( 204 ) )
],
);
}
}

Any request made in the above example will use a response in the provided sequence. There are various helpers such as push_status, push_json, push_body, etc. that can be used to help create a response. You can also pass a Mantle\Testing\Mock_Http_Response object to the push method to push a specific response.

When all the responses in a sequence have been consumed, further requests will throw an exception because there are no remaining responses that can be returned. You can also specify a default response that will be returned when there are no responses left in the sequence:

namespace App\Tests;

use function Mantle\Testing\mock_http_response;
use function Mantle\Testing\mock_http_sequence;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request(
[
'https://github.com/*' => mock_http_sequence()
->push_status( 200 )
->push_status( 400 )
->push_status( 500 )
->when_empty( mock_http_response()->with_json( [ 4, 5, 6 ] )
],
);
}
}

Faking a Specific HTTP Method

By default, a HTTP request will be faked regardless of the method used. If you want to fake a request only when a specific method is used, you can pass the method argument to the $this->fake_request() method:

namespace App\Tests;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->fake_request( 'https://example.com/', method: 'GET' )
->with_response_code( 200 )
->with_body( 'test body' );

$this->fake_request( 'https://example.com/', method: 'POST' )
->with_response_code( 201 )
->with_body( 'test created' );

// You can now make a remote request and it will return the first fake.
$response = wp_remote_get( 'https://example.com/' );

// You can now make a remote request and it will return the second fake.
$response = wp_remote_post( 'https://example.com/' );
}
}

Generating Responses

Mantle\Testing\Mock_Http_Response class, and the $this->mock_response()/mock_http_response() helpers exists to help you fluently build a mock remote response. The following methods are available to build a response and can be chained together:

with_status( int $status ) / with_response_code( int $status )

Create a response with a specific status code.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_status( 200 );

with_body( string $body )

Create a response with a specific body.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_body( 'test body' );

with_json( $payload )

Create a response with a JSON body and set the Content-Type header to application/json.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_json( [ 1, 2, 3 ] );

with_xml( string $payload )

Create a response with an XML body and set the Content-Type header to application/xml.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_xml( '<xml></xml>' );

with_header( string $key, string $value )

Create a response with a specific header.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_header( 'Content-Type', 'application/json' );

with_headers( array $headers )

Create a response with specific headers.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_headers( [ 'Content-Type' => 'application/json' ] );

Create a response with a specific cookie.

use function Mantle\Testing\mock_http_response;

$cookie = new \WP_Http_Cookie();
$cookie->name = 'test';
$cookie->value = 'value';

mock_http_response()->with_cookie( $cookie );

with_redirect( string $url, int $code = 301 )

Create a response with a specific redirect.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_redirect( 'https://wordpress.org/' );

with_temporary_redirect( string $url )

Create a response with a specific temporary redirect.

use function Mantle\Testing\mock_http_response;

mock_http_response()->with_temporary_redirect( 'https://wordpress.org/' );

with_file( string $path )

Create a response with a file as the response body. The appropriate Content-Type header will be set based on the file extension.

use function Mantle\Testing\mock_http_response;

$this->fake_request( 'https://example.com/file' )
->with_file( '/path/to/file' );

// Use the `mock_http_response` helper if you'd like.
mock_http_response()->with_file( '/path/to/file' );

with_filename( string $filename )

Create a response with a specific filename.

use function Mantle\Testing\mock_http_response;

$this->fake_request( 'https://example.com/file' )
->with_filename( 'test.txt' );

// Use the `mock_http_response` helper if you'd like.
mock_http_response()->with_filename( 'test.txt' );

with_image( ?string $filename = null )

Create a response with an image as the response body. The image will be a JPEG image by default.

use function Mantle\Testing\mock_http_response;

$this->fake_request( 'https://example.com/image' )
->with_image();

// Use the `mock_http_response` helper if you'd like.
mock_http_response()->with_image();

Asserting Requests

All remote requests can be asserted against, even if they're not being faked by the test case. Mantle will always log if an actual remote request is being made during a unit test.

Assertions

assertRequestSent( string|callable|null $url_or_callback = null, int $expected_times = null )

Assert that a request was sent to a specific URL.

namespace App\Tests;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
Http::get( 'https://example.com/' );

$this->assertRequestSent( 'https://example.com/' );
}
}

Sometimes you may want to assert that a request was sent with specific mix of headers/methods/body requirements. This can be done using a callback that is passed the Mantle\Http_Client\Request object:

use Mantle\Facade\Http;
use Mantle\Http_Client\Request;
use Tests\Test_Case;

class Example_Test extends Test_Case {
public function test_example() {
Http::with_basic_auth( 'user', 'pass' )
->get( 'https://example.com/basic-auth/' );

// Assert that a request was sent with an authorization header,
// is a GET request and to a specific URL.
$this->assertRequestSent( fn ( Request $request ) => $request
->has_header( 'Authorization', 'Basic dXNlcjpwYXNz' )
&& 'https://example.com/basic-auth/' === $request->url()
&& 'GET' === $request->method()
);

// Assert that a request was not sent with a specific header,
$this->assertRequestNotSent( fn ( Request $request ) => $request
->has_header( 'Content-Type', 'application-json' )
&& 'https://example.com/get/' === $request->url()
&& 'GET' === $request->method()
);
}
}

assertRequestNotSent( string|callable|null $url_or_callback = null )

Assert that a request was not sent to a specific URL.

namespace App\Tests;

use Tests\Test_Case;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
Http::get( 'https://example.com/' );

$this->assertRequestNotSent( 'https://example.com/not-sent/' );
}
}

assertNoRequestSent()

Assert that no requests were sent.

namespace App\Tests;

use Tests\Test_Case;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
$this->assertNoRequestSent();
}
}

assertRequestCount( int $number )

Assert that a specific number of requests were sent.

namespace App\Tests;

use Tests\Test_Case;

class ExampleRequestTest extends Test_Case {
/**
* Example test.
*/
public function test_example() {
Http::get( 'https://example.com/' );

$this->assertRequestCount( 1 );
}
}

Preventing Stray Requests

If you would like to ensure that all requests are faked during a unit test, you can use the $this->prevent_stray_requests() method. This will throw an RuntimeException instance if any requests are made that do not have a corresponding fake.

namespace App\Tests;

use Tests\Test_Case;

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

$this->prevent_stray_requests();
}

public function test_example() {
$this->fake_request( 'https://alley.com/*' )
->with_response_code( 200 )
->with_body( 'alley!' );

// An 'alley!' response is returned.
Http::get( 'https://alley.com/' );

// An exception is thrown because the request was not faked.
Http::get( 'https://github.com/' );
}
}

Stray requests can be re-enabled by calling $this->allow_stray_requests().

namespace App\Tests;

use Tests\Test_Case;

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

$this->prevent_stray_requests();
}

public function test_example() {
$this->fake_request( 'https://alley.com/*' )
->with_response_code( 200 )
->with_body( 'alley!' );

Http::get( 'https://alley.com/' );

$this->allow_stray_requests();

// An exception is not thrown because stray requests are allowed.
Http::get( 'https://github.com/' );
}
}

You can also use the Mantle\Testing\Concerns\Prevent_Remote_Requests trait to prevent stray requests in all tests in a test case.

namespace App\Tests;

use Tests\Test_Case;

use Mantle\Testing\Concerns\Prevent_Remote_Requests;

class Example_Test extends Test_Case {
use Prevent_Remote_Requests;

public function test_example() {
$this->fake_request( 'https://alley.com/*' )
->with_response_code( 200 )
->with_body( 'alley!' );

// An 'alley!' response is returned.
Http::get( 'https://alley.com/' );

// An exception is thrown because the request was not faked.
Http::get( 'https://github.com/' );
}
}