首页 > 解决方案 > 在 Laravel 中运行功能测试时如何模拟服务(或服务提供者)?

问题描述

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

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

好的,所以这一切都有效。我还编写了一些测试代码,如下所示:

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 了?

标签: phplaravellaravel-5mockingphpunit

解决方案


显而易见的方法是在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或其他)register()

if (app()->environment('testing')) {
    app()->register(FakeUserProvider::class);
} else {
    app()->register(UserProvider::class);
}

注意:缺点是提供者列表在两个地方,一个在 config/app.php,一个在 AppServiceProvider.php


推荐阅读