我正在Laravel中编写一个小API,部分原因是为了学习这个框架。我想我已经在文档中发现了一个漏洞,但这可能是因为我不理解“Laravel方式”来做我想做的事。
我正在编写一个HTTP API,其中包括在Linux服务器上列出,创建和删除系统用户。结构是这样的:
/v1/users
分别将GET
,POST
和DELETE
动词连接到控制器方法get
,create
和delete
App\Http\Controllers\UserController
实际上并不运行系统调用,而是由服务App\Services\Users
完成。App\Providers\Server\Users
创建,该服务在延迟的基础上注册singleton
服务。好的,所以这一切都有效。我也写了一些测试代码,如下:
public function testGetUsers()
{
$response = $this->json('GET', '/v1/users');
/* @var $response \Illuminate\Http\JsonResponse */
$response
->assertStatus(200)
->assertJson(['ok' => true, ]);
}
这也很好。但是,这使用UserService
的常规绑定,我想在这里放置一个虚拟/模拟。
我想我需要将UserService
更改为界面,这很容易,但我不知道如何告诉底层测试系统我希望它运行我的控制器,但是非标准服务。我在研究这个问题时看到了App::bind()
在Stack Overflow答案中出现,但是App
并不是在工匠生成的测试中自动调整范围,所以感觉就像抓着稻草一样。
如何实例化虚拟服务,然后在测试时将其发送到Laravel,因此它不使用标准的ServiceProvider?
答案 0 :(得分:7)
显而易见的方法是在setUp()
中重新绑定实现。
让自己成为新的UserTestCase
(或编辑Laravel提供的那个)并添加:
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp()
{
parent::setUp();
app()->bind(YourService::class, function() { // not a service provider but the target of service provider
return new YourFakeService();
});
}
}
class YourFakeService {} // I personally keep fakes in the test files itself if they are short
根据环境有条件地注册提供商(将其放在 AppServiceProvider.php 或您为此任务指定的任何其他提供商 - ConditionalLoaderServiceProvider.php 或其他){{ 1}}方法
register()
注意:缺点是提供商的 list 位于config / app.php中的两个位置,AppServiceProvider.php中的一个位置
答案 1 :(得分:3)
<?php
namespace Tests\Feature;
use Tests\TestCase;
use \App\Services\Users as UsersService;
class UsersTest extends TestCase
{
/**
* Checks the listing of users
*
* @return void
*/
public function testGetUsers()
{
$this->app->bind(UsersService::class, function() {
return new UsersDummy();
});
$response = $this->json('GET', '/v1/users');
$response
->assertStatus(200)
->assertJson(['ok' => true, ]);
}
}
class UsersDummy extends UsersService
{
public function listUsers()
{
return ['tom', 'dick', 'harry', ];
}
}
这会注入一个DI绑定,因此默认的ServiceProvider不需要启动。如果我将一些调试代码添加到$response
,如下所示:
/* @var $response \Illuminate\Http\JsonResponse */
print_r($response->getData(true));
然后我得到了这个输出:
Array
(
[ok] => 1
[users] => Array
(
[0] => tom
[1] => dick
[2] => harry
)
)
这使我能够创建一个带有围绕PHP绘制边界的测试,并且不会对测试框进行调用以与用户系统进行交互。
接下来我将研究我的控制器构造函数是否可以从具体的实现提示(\App\Services\Users
)更改为接口,这样我的测试实现就不需要从实际的扩展中扩展。 / p>