在 Laravel 中运行功能测试时如何模拟服务(或服务提供者)?
我正在 Laravel 中编写一个小型 API,部分是为了学习这个框架.我想我在文档中发现了一个巨大的漏洞,但这可能是因为我不理解Laravel 方式"来做我想做的事.
我正在编写一个 HTTP API,其中包括列出、创建和删除 Linux 服务器上的系统用户.结构是这样的:
- 到
/v1/users
的路由将GET
、POST
和DELETE
动词连接到控制器方法分别获取
、create
和delete
. - 控制器
AppHttpControllersUserController
并不实际运行系统调用,这是由服务AppServicesUsers
完成的. - 服务由 ServiceProvider
AppProvidersServerUsers
创建,该服务提供者在延迟的基础上注册服务的singleton
. - 服务由 Laravel 自动实例化并自动注入到控制器的构造函数中.
好的,所以这一切都有效.我也写了一些测试代码,像这样:
公共函数 testGetUsers(){$response = $this->json('GET', '/v1/users');/* @var $response IlluminateHttpJsonResponse */$响应->断言状态(200)->assertJson(['ok' => true, ]);}
这也很好用.但是,这使用了 UserService
的普通绑定,我想在这里放一个 dummy/mock.
我想我需要将我的 UserService
更改为一个接口,这很容易,但我不确定如何告诉底层测试系统我希望它运行我的控制器,但是使用非标准服务.我在研究此问题时看到 App::bind()
出现在 Stack Overflow 的答案中,但 App
在工匠生成的测试中不会自动出现在范围内,所以感觉就像在抓紧在稻草上.
如何实例化一个虚拟服务,然后在测试时将其发送到 Laravel,这样它就不会使用标准的 ServiceProvider 来代替?
解决方案显而易见的方法是在 setUp()
中重新绑定实现.
让你自己成为一个新的 UserTestCase
(或者编辑 Laravel 提供的那个)并添加:
抽象类 TestCase 扩展 BaseTestCase{使用 CreatesApplication;受保护的函数 setUp(){父::setUp();app()->bind(YourService::class, function() {//不是服务提供者而是服务提供者的目标返回新的 YourFakeService();});}}class YourFakeService {}//如果测试文件很短,我个人会将假文件保存在测试文件中
<小时>
在 register()
方法
if (app()->environment('testing')) {app()->register(FakeUserProvider::class);} 别的 {app()->register(UserProvider::class);}
<块引用>
注意:缺点是提供者的 list 位于两个位置,一个在 config/app.php,一个在 AppServiceProvider.php
I'm writing a small API in Laravel, partly for the purposes of learning this framework. I think I have spotted a gaping hole in the docs, but it may be due to my not understanding the "Laravel way" to do what I want.
I am writing an HTTP API to, amongst other things, list, create and delete system users on a Linux server. The structure is like so:
- Routes to
/v1/users
connectGET
,POST
andDELETE
verbs to controller methodsget
,create
anddelete
respectively. - The controller
AppHttpControllersUserController
does not actually run system calls, that is done by a serviceAppServicesUsers
. - The service is created by a ServiceProvider
AppProvidersServerUsers
that registers asingleton
of the service on a deferred basis. - The service is instantiated by Laravel automatically and auto-injected into the controller's constructor.
OK, so this all works. I have also written some test code, like so:
public function testGetUsers()
{
$response = $this->json('GET', '/v1/users');
/* @var $response IlluminateHttpJsonResponse */
$response
->assertStatus(200)
->assertJson(['ok' => true, ]);
}
This also works fine. However, this uses the normal bindings for the UserService
, and I want to put a dummy/mock in here instead.
I think I need to change my UserService
to an interface, which is easy, but I am not sure how to tell the underlying test system that I want it to run my controller, but with a non-standard service. I see App::bind()
cropping up in Stack Overflow answers when researching this, but App
is not automatically in scope in artisan-generated tests, so it feels like clutching at straws.
How can I instantiate a dummy service and then send it to Laravel when testing, so it does not use the standard ServiceProvider instead?
解决方案The obvious way is to re-bind the implementation in setUp()
.
Make your self a new UserTestCase
(or edit the one provided by Laravel) and add:
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
Register providers conditionally based on environment (put this in AppServiceProvider.php or any other provider that you designate for this task - ConditionalLoaderServiceProvider.php or whatever) in register()
method
if (app()->environment('testing')) {
app()->register(FakeUserProvider::class);
} else {
app()->register(UserProvider::class);
}
Note: drawback is that list of providers is on two places one in config/app.php and one in the AppServiceProvider.php
相关文章