From f235bf84982c297e1de479ad5e75c1345424c932 Mon Sep 17 00:00:00 2001 From: Sean Fisher Date: Fri, 15 Jul 2022 15:58:00 -0400 Subject: [PATCH] Prevent stray requests during unit tests --- .../trait-interacts-with-requests.php | 37 +++++++++++++++++++ .../test-interacts-with-external-requests.php | 28 ++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/mantle/testing/concerns/trait-interacts-with-requests.php b/src/mantle/testing/concerns/trait-interacts-with-requests.php index 6edd740df..678888afe 100644 --- a/src/mantle/testing/concerns/trait-interacts-with-requests.php +++ b/src/mantle/testing/concerns/trait-interacts-with-requests.php @@ -16,8 +16,10 @@ use Mantle\Support\Str; use Mantle\Testing\Mock_Http_Response; use PHPUnit\Framework\Assert as PHPUnit; +use RuntimeException; use function Mantle\Support\Helpers\collect; +use function Mantle\Support\Helpers\value; /** * Allow Mock HTTP Requests @@ -37,6 +39,13 @@ trait Interacts_With_Requests { */ protected $recorded_requests; + /** + * Flag to prevent external requests from being made. + * + * @var Mock_Http_Response|\Closure|bool + */ + protected $preventing_stray_requests = false; + /** * Setup the trait. */ @@ -65,6 +74,8 @@ public function interacts_with_requests_tear_down() { * @param string $url The request URL. * @return mixed Array if the request has been preempted, any value that's * not false otherwise. + * + * @throws RuntimeException If the request was made without a matching faked request. */ public function pre_http_request( $preempt, $request_args, $url ) { $this->recorded_requests[] = new Request( $request_args, $url ); @@ -82,6 +93,16 @@ public function pre_http_request( $preempt, $request_args, $url ) { } } + if ( false !== $this->preventing_stray_requests ) { + $prevent = value( $this->preventing_stray_requests ); + + if ( $prevent instanceof Mock_Http_Response ) { + return $prevent->to_array(); + } + + throw new RuntimeException( "Attempted request to [{$url}] without a matching fake." ); + } + // To aid in debugging, print a message to the console that this test is making an actual HTTP request // which it probably shouldn't be. printf( @@ -151,6 +172,22 @@ function( $response, $url_or_callback ) { return $response; } + /** + * Prevent stray external requests. + * + * @param Mock_Http_Response|\Closure|bool $response A default response or callback to use, boolean otherwise. + */ + public function prevent_stray_requests( $response = true ) { + $this->preventing_stray_requests = $response; + } + + /** + * Allow stray external requests. + */ + public function allow_stray_requests() { + $this->preventing_stray_requests = false; + } + /** * Retrieve a callback for the stubbed response. * diff --git a/tests/testing/concerns/test-interacts-with-external-requests.php b/tests/testing/concerns/test-interacts-with-external-requests.php index bcf33e141..9347c7cde 100644 --- a/tests/testing/concerns/test-interacts-with-external-requests.php +++ b/tests/testing/concerns/test-interacts-with-external-requests.php @@ -1,6 +1,7 @@ assertEquals( 202, $http->get( 'https://example.com/sequence/' )->status() ); $this->assertEquals( 202, $http->get( 'https://example.com/sequence/' )->status() ); } + + public function test_prevent_stray_requests() { + $this->prevent_stray_requests( + Mock_Http_Response::create()->with_status( 201 ), + ); + + $this->assertEquals( 201, Http::get( 'https://example.com/' )->status() ); + $this->assertRequestSent(); + } + + public function test_prevent_stray_requests_callback() { + $this->prevent_stray_requests( + fn () => Mock_Http_Response::create()->with_status( 400 ), + ); + + $this->assertEquals( 400, Http::get( 'https://example.com/' )->status() ); + $this->assertRequestSent(); + } + + public function test_prevent_stray_requests_no_fallback() { + $this->expectException( RuntimeException::class ); + $this->expectExceptionMessage( 'Attempted request to [https://example.org/path/] without a matching fake.' ); + + $this->prevent_stray_requests(); + + Http::get( 'https://example.org/path/' ); + } }