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.
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.
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 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 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 mock_http_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_status( 200 )
->push_status( 400 )
->push_status( 500 )
],
);
}
}
Any request made in the above example will use a response in the defined sequence. 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( mock_http_response()->with_json( [ 1, 2, 3 ] )
->push( mock_http_response()->with_json( [ 4, 5, 6 ] )
->when_empty( mock_http_response()->with_json( [ 4, 5, 6 ] )
],
);
}
}
Generating Responses
Mantle\Testing\Mock_Http_Response
class and mock_http_response()
helper
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' ] );
with_cookie( \WP_Http_Cookie $cookie )
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;
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;
mock_http_response()->with_filename( 'test.txt' );
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/' );
}
}