在 Laravel 中运行功能测试时如何模拟服务(或服务提供者)? [英] How to mock a service (or a ServiceProvider) when running Feature tests in Laravel?

查看:37
本文介绍了在 Laravel 中运行功能测试时如何模拟服务(或服务提供者)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 Laravel 中编写一个小型 API,部分是为了学习这个框架.我想我在文档中发现了一个巨大的漏洞,但这可能是因为我不理解Laravel 方式"来做我想做的事.

我正在编写一个 HTTP API,其中包括列出、创建和删除 Linux 服务器上的系统用户.结构是这样的:

  • /v1/users 的路由将 GETPOSTDELETE 动词连接到控制器方法 分别获取createdelete.
  • 控制器 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 connect GET, POST and DELETE verbs to controller methods get, create and delete respectively.
  • The controller AppHttpControllersUserController does not actually run system calls, that is done by a service AppServicesUsers.
  • The service is created by a ServiceProvider AppProvidersServerUsers that registers a singleton 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

这篇关于在 Laravel 中运行功能测试时如何模拟服务(或服务提供者)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆