↪️ Bypass for PHP creates a custom HTTP Server to return predefined responses to client requests

Overview

Bypass Logo

Bypass for PHP

About | Installation | Writing Tests | Examples | Credits | Inspired

US flag in base64 BR flag in base64

PHP Composer GitHub tag (latest by date) Packagist Downloads Packagist License Last Updated


About

Bypass for PHP provides a quick way to create a custom HTTP Server to return predefined responses to client requests.

This is useful in tests when your application make requests to external services, and you need to simulate different situations like returning specific data or unexpected server errors.


Installation

📌 Bypass requires PHP 8.0+.

To install via composer, run the following command:

composer require --dev ciareis/bypass

Writing Tests

Content

📝 Note: If you wish to view full codes, head to the Examples section.

1. Open a Bypass Service

To write a test, first open a Bypass server:

//Opens a new Bypass server
$bypass = Bypass::open();

Bypass will always run at http://localhost listening to a random port number.

If needed, a port can be specified passing it as an argument (int) $port:

//Opens a new Bypass using port 8081
$bypass = Bypass::open(8081);

2. Bypass URL and Port

The Bypass server URL can be retrieved with getBaseUrl():

$bypassUrl = $bypass->getBaseUrl(); //http://localhost:16819

If you need to retrieve only the port number, use the getPort() method:

$bypassPort = $bypass->getPort(); //16819

3. Routes

Bypass serves two types of routes: The Standard Route, which can return a text body content and the File Route, which returns a binary file.

When running your tests, you will inform Bypass routes to Application or Service, making it access Bypass URLs instead of the real-world URLs.

3.1 Standard Route

addRoute(method: 'get', uri: '/v1/demo', status: 200, body: $body); //Instantiates a DemoService class $service = new DemoService(); //Consumes the service using the Bypass URL $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getTotal(); //Your test assertions here... ">
//Json body
$body = '{"name": "John", "total": 1250}';

//Route retuning the JSON body with HTTP Status 200
$bypass->addRoute(method: 'get', uri: '/v1/demo', status: 200, body: $body);

//Instantiates a DemoService class
$service = new DemoService();

//Consumes the service using the Bypass URL
$response = $service->setBaseUrl($bypass->getBaseUrl())
  ->getTotal();

//Your test assertions here...

The method addRoute() accepts the following parameters:

Parameter Type Description
HTTP Method int $method HTTP Request Method (GET/POST/PUT/PATCH/DELETE)
URI string $uri URI to be served by Bypass
Status int $status HTTP Status Code to be returned by Bypass (default: 200)
Body string|array $body Body to be served by Bypass (optional)
Times int $times How many times the route should be called (default: 1)

3.2 File Route

//Reads a PDF file
$demoFile = \file_get_contents('storage/pdfs/demo.pdf');

//File Route returning a binary file with HTTP Status 200
$bypass->addFileRoute(method: 'get', uri: '/v1/myfile', status: 200, file: $demoFile);

//Instantiates a DemoService class
$service = new DemoService();

//Consumes the service using the Bypass URL
$response = $service->setBaseUrl($bypass->getBaseUrl())
  ->getPdf();

//Your test assertions here...

The method addFileRoute() accepts the following parameters:

Parameter Type Description
HTTP Method int $method HTTP Request Method (GET/POST/PUT/PATCH/DELETE)
URI string $uri URI to be served by Bypass
Status int $status HTTP Status Code to be returned by Bypass (default: 200)
File binary $file Binary file to be served by Bypass
Times int $times How many times the route should be called (default: 1)

4. Asserting Route Calling

You might need to assert that a route was called at least one or multiple times.

The method assertRoutes() will return a RouteNotCalledException if a route was NOT called as many times as defined in the $times parameter.

If you need to assert that a route is NOT being called by your service, set the parameter $times = 0

addRoute(method: 'get', uri: '/v1/demo', status: 200, body: $body, times: 2); //Instantiates a DemoService class $service = new DemoService(); //Consumes the service using the Bypass URL $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getTotal(); $bypass->assertRoutes(); //Your test assertions here... ">
//Json body
$body = '{"name": "John", "total": 1250}';

//Defines a route which must be called two times
$bypass->addRoute(method: 'get', uri: '/v1/demo', status: 200, body: $body, times: 2);

//Instantiates a DemoService class
$service = new DemoService();

//Consumes the service using the Bypass URL
$response = $service->setBaseUrl($bypass->getBaseUrl())
  ->getTotal();

$bypass->assertRoutes();

//Your test assertions here...

5. Stop or shut down

Bypass will automatically stop its server once your test is done running.

The Bypass server can be stopped or shut down at any point with the following methods:

To stop: $bypass->stop();

To shut down: $bypass->down();

Examples

Use case

To better illustrate Bypass usage, imagine you need to write a test for a service called TotalScoreService. This service calculates the total game score of a given username. To get the score is obtained making external request to a fictitious API at emtudo-games.com/v1/score/::USERNAME::. The API returns HTTP Status 200 and a JSON body with a list of games:

{
  "games": [
    {
      "id": 1,
      "points": 25
    },
    {
      "id": 2,
      "points": 10
    }
  ],
  "is_active": true
}
addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body); //Instantiates a TotalScoreService $service = new TotalScoreService(); //Consumes the service using the Bypass URL $response = $serivce ->setBaseUrl($bypassUrl) // set the URL to the Bypass URL ->getTotalScoreByUsername('johndoe'); //returns 35 //Pest PHP verify that response is 35 expect($response)->toBe(35); //PHPUnit verify that response is 35 $this->assertSame($response, 35); ">
//Opens a new Bypass server
$bypass = Bypass::open();

//Retrieves the Bypass URL
$bypassUrl = $bypass->getBaseUrl();

//Json body
$body = '{"games":[{"id":1, "name":"game 1","points":25},{"id":2, "name":"game 2","points":10}],"is_active":true}';

//Defines a route
$bypass->addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body);
    
//Instantiates a TotalScoreService
$service = new TotalScoreService();

//Consumes the service using the Bypass URL
$response = $serivce
  ->setBaseUrl($bypassUrl) // set the URL to the Bypass URL
  ->getTotalScoreByUsername('johndoe'); //returns 35

//Pest PHP verify that response is 35
expect($response)->toBe(35);

//PHPUnit verify that response is 35
$this->assertSame($response, 35);

Quick Test Examples

Click below to see code snippets for Pest PHP and PHPUnit.

Pest PHP
addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body); //Instantiates and consumes the service using the Bypass URL $service = new TotalScoreService(); $response = $service ->setBaseUrl($bypass->getBaseUrl()) ->getTotalScoreByUsername('johndoe'); //Verifies that response is 35 expect($response)->toBe(35); }); it('properly gets the logo', function () { //Opens a new Bypass server $bypass = Bypass::open(); //Reads the file $filePath = 'docs/img/logo.png'; $file = \file_get_contents($filePath); //Defines a route $bypass->addFileRoute(method: 'get', uri: $filePath, status: 200, file: $file); //Instantiates and consumes the service using the Bypass URL $service = new LogoService(); $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getLogo(); // asserts expect($response)->toEqual($file); }); ">
it('properly returns the total score by username', function () {

  //Opens a new Bypass server
  $bypass = Bypass::open();

  //Json body
  $body = '{"games":[{"id":1, "name":"game 1","points":25},{"id":2, "name":"game 2","points":10}],"is_active":true}';

  //Defines a route
  $bypass->addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body);

  //Instantiates and consumes the service using the Bypass URL
  $service = new TotalScoreService();
  $response = $service
    ->setBaseUrl($bypass->getBaseUrl())
    ->getTotalScoreByUsername('johndoe');

  //Verifies that response is 35
  expect($response)->toBe(35);
});

it('properly gets the logo', function () {

  //Opens a new Bypass server
  $bypass = Bypass::open();

  //Reads the file
  $filePath = 'docs/img/logo.png';
  $file = \file_get_contents($filePath);

  //Defines a route
  $bypass->addFileRoute(method: 'get', uri: $filePath, status: 200, file: $file);

  //Instantiates and consumes the service using the Bypass URL
  $service = new LogoService();
  $response = $service->setBaseUrl($bypass->getBaseUrl())
    ->getLogo();

  // asserts
  expect($response)->toEqual($file);
});
PHPUnit
addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body); //Instantiates and consumes the service using the Bypass URL $service = new TotalScoreService(); $response = $service ->setBaseUrl($bypass->getBaseUrl()) ->getTotalScoreByUsername('johndoe'); //Verifies that response is 35 $this->assertSame(35, $response); } public function test_gets_logo(): void { //Opens a new Bypass server $bypass = Bypass::open(); //Reads the file $filePath = 'docs/img/logo.png'; $file = \file_get_contents($filePath); //Defines a route $bypass->addFileRoute(method: 'get', uri: $filePath, status: 200, file: $file); //Instantiates and consumes the service using the Bypass URL $service = new LogoService(); $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getLogo(); $this->assertSame($response, $file); } } ">
class BypassTest extends TestCase
{
  public function test_total_score_by_username(): void
  {
    //Opens a new Bypass server
    $bypass = Bypass::open();
    
    //Json body
    $body = '{"games":[{"id":1,"name":"game 1","points":25},{"id":2,"name":"game 2","points":10}],"is_active":true}';

    //Defines a route
    $bypass->addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body);

    //Instantiates and consumes the service using the Bypass URL
    $service = new TotalScoreService();
    $response = $service
      ->setBaseUrl($bypass->getBaseUrl())
      ->getTotalScoreByUsername('johndoe');
    
    //Verifies that response is 35 
    $this->assertSame(35, $response);
  }

  public function test_gets_logo(): void
  {
    //Opens a new Bypass server
    $bypass = Bypass::open();

    //Reads the file
    $filePath = 'docs/img/logo.png';
    $file = \file_get_contents($filePath);

    //Defines a route
    $bypass->addFileRoute(method: 'get', uri: $filePath, status: 200, file: $file);

    //Instantiates and consumes the service using the Bypass URL
    $service = new LogoService();
    $response = $service->setBaseUrl($bypass->getBaseUrl())
      ->getLogo();

    $this->assertSame($response, $file);
  }
}

Test Examples

📚 See Bypass being used in complete tests with Pest PHP and PHPUnit for the GithubRepoService demo service.

Credits

And a special thanks to @DanSysAnalyst

Inspired

Code inspired by Bypass

You might also like...
Declarative HTTP Clients using Guzzle HTTP Library and PHP 8 Attributes

Waffler How to install? $ composer require waffler/waffler This package requires PHP 8 or above. How to test? $ composer phpunit Quick start For our e

Guzzle, an extensible PHP HTTP client
Guzzle, an extensible PHP HTTP client

Guzzle, PHP HTTP client Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and trivial to integrate with web services. Simple interf

A Chainable, REST Friendly, PHP HTTP Client. A sane alternative to cURL.

Httpful Httpful is a simple Http Client library for PHP 7.2+. There is an emphasis of readability, simplicity, and flexibility – basically provide the

PHP's lightweight HTTP client

Buzz - Scripted HTTP browser Buzz is a lightweight (1000 lines of code) PHP 7.1 library for issuing HTTP requests. The library includes three clients

HTTPlug, the HTTP client abstraction for PHP

HTTPlug HTTPlug, the HTTP client abstraction for PHP. Intro HTTP client standard built on PSR-7 HTTP messages. The HTTPlug client interface is compati

Unirest in PHP: Simplified, lightweight HTTP client library.

Unirest for PHP Unirest is a set of lightweight HTTP libraries available in multiple languages, built and maintained by Mashape, who also maintain the

A PHP proxy to solve client browser HTTP CORS(cross-origin) restrictions.

cors-bypass-proxy A PHP proxy to solve client browser HTTP CORS(cross-origin) restrictions. A simple way to solve CORS issue when you have no access t

Zenscrape package is a simple PHP HTTP client-provider that makes it easy to parsing site-pages

Zenscrape package is a simple PHP HTTP client-provider that makes it easy to parsing site-pages

Async HTTP/1.1+2 client for PHP based on Amp.
Async HTTP/1.1+2 client for PHP based on Amp.

This package provides an asynchronous HTTP client for PHP based on Amp. Its API simplifies standards-compliant HTTP resource traversal and RESTful web

Comments
  • fix typo, indentation, closing html tags, among others fix.

    fix typo, indentation, closing html tags, among others fix.

    Changes on Bypass English Documentation

    • Rename file in uppercase;
    • Fix typo;
    • Arrange identation;
    • Swap TABS for SPACES;
    • Add ALT attributes in IMG tags;
    • Close IMG tags;
    • Removal of unnecessary spaces
    opened by vs0uz4 2
  • Fix typo and identation and added status badges for package.

    Fix typo and identation and added status badges for package.

    A fix typo in the service declaration and a small adjustment in the identification of the code blocks, in addition to the insertion of some 'status badges'.

    opened by vs0uz4 1
  • [DOC] Update PT-BR Documentation and Small fixes.

    [DOC] Update PT-BR Documentation and Small fixes.

    Hello boss,

    Follows the portuguese documentation update for Bypass serve()method, Route Helpers and small fixes.

    • Added Bypass->serve() method in doc;
    • Added Route Helpers in doc;
    • Small fixes in doc.
    opened by vs0uz4 0
  • documentation proposal in Portuguese

    documentation proposal in Portuguese

    A simple contribution.

    In this, pull request i am proposing a version of the documentation in PT-BR. I take this opportunity to include some status badges for the package. Like for example:

    • PHP Composer Build Status;
    • Version;
    • Number of Downloads;
    • License;
    • Last commit;
    opened by vs0uz4 0
Requests for PHP is a humble HTTP request library. It simplifies how you interact with other sites and takes away all your worries.

Requests for PHP Requests is a HTTP library written in PHP, for human beings. It is roughly based on the API from the excellent Requests Python librar

null 3.5k Dec 31, 2022
Requests for PHP is a humble HTTP request library. It simplifies how you interact with other sites and takes away all your worries.

Requests for PHP Requests is a HTTP library written in PHP, for human beings. It is roughly based on the API from the excellent Requests Python librar

null 3.5k Dec 31, 2022
PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs

PHP Curl Class: HTTP requests made easy PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs. Installation Requirements Quic

null 3.1k Jan 5, 2023
🐼 Framework agnostic package using asynchronous HTTP requests and PHP generators to load paginated items of JSON APIs into Laravel lazy collections.

Framework agnostic package using asynchronous HTTP requests and generators to load paginated items of JSON APIs into Laravel lazy collections.

Andrea Marco Sartori 61 Dec 3, 2022
Requests - a HTTP library written in PHP, for human beings

Requests is a HTTP library written in PHP, for human beings. It is roughly based on the API from the excellent Requests Python library. Requests is ISC Licensed (similar to the new BSD license) and has no dependencies, except for PHP 5.6+.

WordPress 3.5k Jan 6, 2023
PHP Curl - This package can send HTTP requests to a given site using Curl.

PHP Curl This package can send HTTP requests to a given site using Curl. It provides functions that can take several types of parameters to configure

Mehmet Can 1 Oct 27, 2022
Application for logging HTTP and DNS Requests

Request Logger Made by Adam Langley ( https://twitter.com/adamtlangley ) What is it? Request logger is a free and open source utility for logging HTTP

null 13 Nov 28, 2022
librestful is a virion for PocketMine servers that make easier, readable code and for async http requests.

librestful is a virion for PocketMine servers that make easier, readable code for async rest requests.

RedMC Network 17 Oct 31, 2022
Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests

laminas-http provides the HTTP message abstraction used by laminas-mvc, and also provides an extensible, adapter-driven HTTP client library.

Laminas Project 33 Aug 27, 2022
Event-driven, streaming HTTP client and server implementation for ReactPHP

HTTP Event-driven, streaming HTTP client and server implementation for ReactPHP. This HTTP library provides re-usable implementations for an HTTP clie

ReactPHP 640 Dec 29, 2022