About | Installation | Writing Tests | Examples | Credits | Inspired
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
To install via composer, run the following command:
composer require --dev ciareis/bypass
Writing Tests
Content
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
//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
//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
}
//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
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
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
Credits
And a special thanks to @DanSysAnalyst
Inspired
Code inspired by Bypass