ciareis / bypass
Bypass for PHP 提供了一种快速方式,用于创建一个自定义 HTTP 服务器,而不是实际的服务器,以返回预制的响应给客户端请求。这在测试中非常有用,当你想要创建一个模拟 HTTP 服务器并测试你的 HTTP 客户端如何处理来自服务器不同类型的响应时。
Requires
- php: ^8.1
- illuminate/support: ^9.0 || ^10.0 || ^11.0
- symfony/process: ^5.2.0 || ^6.0 || ^7.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.5
- laravel/framework: ^9.0|^10.0|^11.0
- orchestra/testbench: ^7.0|^8.0|^9.0
- pestphp/pest: ^2.1
- phpunit/phpunit: ^10
README
Bypass for PHP
关于 | 安装 | 编写测试 | 示例 | 鸣谢 | 灵感来源
关于
安装
📌 Bypass 需要 PHP 8.0+。
通过 composer 安装,请运行以下命令
composer require --dev ciareis/bypass
视频演示
编写测试
内容
🔥 在以下部分查看完整的代码示例 这里。
1. 打开 Bypass 服务器
要编写测试,首先打开 Bypass 服务器
//Open a new Bypass server $bypass = Bypass::open();
Bypass 将始终在 http://localhost
上运行,监听随机端口。
要指定自定义端口,只需在参数 (int) $port
中传递它。
//Open a new Bypass using port 8081 $bypass = Bypass::open(8081);
2. Bypass URL 和端口
您可以使用 getBaseUrl()
获取 Bypass 服务器 URL。
$bypassUrl = $bypass->getBaseUrl(); //http://localhost:16819
如果您只需要获取端口号,请使用 getPort()
方法
$bypassPort = $bypass->getPort(); //16819
3. 路由
Bypass 提供两种类型的路由:用于返回文本正文内容的 标准路由
和返回二进制文件的 文件路由
。
在运行测试套件时,您应该将 Bypass 创建的 URL 传递给您的服务。这样,您将使您正在测试的服务达到 Bypass 而不是达到现实世界的 API 终端。
3.1 标准路由
use Ciareis\Bypass\Bypass; //Json body $body = '{"username": "john", "name": "John Smith", "total": 1250}'; //Route retuning the JSON body with HTTP Status 200 $bypass->addRoute(method: 'GET', uri: '/v1/demo/john', status: 200, body: $body); //Instantiates a DemoService class $service = new DemoService(); //Configure your service to access Bypass URL $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getTotalByUser('john'); //Your test assertions here...
addRoute()
方法接受以下参数
3.2 文件路由
use Ciareis\Bypass\Bypass; //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(); //Configure your service to access Bypass URL $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getPdf(); //Your test assertions here...
addFileRoute()
方法接受以下参数
3.3 Bypass Serve 和路由助手
Bypass 为您提供便捷的快捷方式,用于最常见的 HTTP 请求。
这些快捷方式称为“路由助手”,并使用 Bypass::serve()
在随机端口上自动提供,无需调用 Bypass::open()
。
在下一个示例中,Bypass 提供了两个路由:一个可通过 GET
方法访问的 URL,返回状态为 200
的 JSON 正文,以及一个第二个可通过 GET
方法访问的路由 URL,返回状态 404
。
use Ciareis\Bypass\Bypass; use Ciareis\Bypass\Route; //Create and serve routes $bypass = Bypass::serve( Route::ok(uri: '/v1/demo/john', body: ['username' => 'john', 'name' => 'John Smith', 'total' => 1250]), //method GET, status 200 Route::notFound(uri: '/v1/demo/wally') //method GET, status 404 ); //Instantiates a DemoService class $service = new DemoService(); $service->setBaseUrl($bypass->getBaseUrl()); //Consumes the "OK (200)" route $responseOk = $service->getTotalByUser('john'); //200 - OK with total => 1250 //Consumes the "Not Found (404)" route $responseNotFound = $service->getTotalByUser('wally'); //404 - Not found //Your test assertions here...
路由助手
以下列出了路由助手列表。
您还可以通过传递参数来根据您的需求调整助手
在下面的示例中,您可以看到使用方法 GET
的助手 Route::badRequest
而不是其默认方法 POST
。
use Ciareis\Bypass\Bypass; use Ciareis\Bypass\Route; Bypass::serve( Route::badRequest(uri: '/v1/users?filter=foo', body: ['error' => 'Filter parameter foo is not allowed.'], method: 'GET') );
📝 注意:如果助手未涵盖您需要的某些内容,可以使用 标准路由 创建自定义路由。
4. 断言路由调用
有时您可能需要断言某个路由至少被调用一次或多次。
方法 assertRoutes()
如果某个路由未被调用次数与在 $times
参数中定义的次数不符,将返回 RouteNotCalledException
。
如果您需要断言您的服务没有调用某个路由,请设置参数 $times = 0
//Json body $body = '{"username": "john", "name": "John Smith", "total": 1250}'; //Defines a route which must be called two times $bypass->addRoute(method: 'GET', uri: '/v1/demo/john', 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()) ->getTotalByUser('john'); $bypass->assertRoutes(); //Your test assertions here...
5. 停止或关闭
Bypass 在测试完成后将自动停止其服务器。
可以使用以下方法在任何时候停止或关闭 Bypass 服务器
停止: $bypass->stop();
关闭: $bypass->down();
示例
用例
为了更好地说明 Bypass 的使用,假设您需要编写一个测试,该测试针对一个计算给定用户名的游戏总分数的服务。
分数是通过向位于 emtudo-games.com/v1/score/::USERNAME::
的虚构 API 发送外部请求获得的。API 返回 HTTP 状态码 200
和包含游戏列表的 JSON 主体
{ "games": [ { "id": 1, "points": 25 }, { "id": 2, "points": 10 } ], "is_active": true }
use Ciareis\Bypass\Bypass; //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(); //Configure your service to access Bypass URL $response = $serivce ->setBaseUrl($bypassUrl) // set the URL to the Bypass URL ->getTotalScoreByUsername('johndoe'); //returns 35 //Pest PHP verifies that response is 35 expect($response)->toBe(35); //PHPUnit verifies that response is 35 $this->assertSame($response, 35);
快速测试示例
点击下面查看 Pest PHP 和 PHPUnit 的代码片段。
Pest PHP
use Ciareis\Bypass\Bypass; 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); //Configure your service to access 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); //Configure your service to access Bypass URL $service = new LogoService(); $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getLogo(); // asserts expect($response)->toEqual($file); });
PHPUnit
use Ciareis\Bypass\Bypass; 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); //Configure your service to access 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); //Configure your service to access Bypass URL $service = new LogoService(); $response = $service->setBaseUrl($bypass->getBaseUrl()) ->getLogo(); $this->assertSame($response, $file); } }
测试示例
📚 在完整的测试中查看 Bypass 的使用,请参阅 Pest PHP 和 PHPUnit,以及 GithubRepoService 演示服务。
致谢
并向 @DanSysAnalyst 表示特别感谢
灵感来源
代码灵感来源于 Bypass