简体   繁体   English

Laravel,测试时如何给服务容器另一种实现?

[英]In Laravel, how to give another implementation to the service container when testing?

I'm creating a Laravel controller where a Random string generator interface gets injected to one of the methods.我正在创建一个 Laravel controller,其中一个随机字符串生成器接口被注入其中一个方法。 Then in AppServiceProvider I'm registering an implementation.然后在 AppServiceProvider 中我注册了一个实现。 This works fine.这很好用。

The controller uses the random string as input to save data to the database. controller 使用随机字符串作为输入将数据保存到数据库中。 Since it's random, I can't test it (using MakesHttpRequests ) like so:因为它是随机的,所以我无法像这样测试它(使用MakesHttpRequests ):

$this->post('/api/v1/do_things', ['email' => $this->email])
->seeInDatabase('things', ['email' => $this->email, 'random' => 'abc123']);

because I don't know what 'abc123' will be when using the actual random generator.因为我不知道在使用实际的随机生成器时“abc123”会是什么。 So I created another implementation of the Random interface that always returns 'abc123' so I could assert against that.所以我创建了另一个 Random 接口的实现,它总是返回“abc123”,所以我可以断言。

Question is: how do I bind to this fake generator at testing time?问题是:我如何在测试时绑定到这个假的生成器? I tried to do我试着做

$this->app->bind('Random', 'TestableRandom');

right before the test, but it still uses the actual generator that I register in AppServiceProvider.就在测试之前,但它仍然使用我在 AppServiceProvider 中注册的实际生成器。 Any ideas?有任何想法吗? Am I on the wrong track completely regarding how to test such a thing?关于如何测试这样的事情,我是否完全走错了路?

Thanks!谢谢!

You have a couple options: 你有几个选择:

Use a conditional to bind the implementation: 使用条件绑定实现:

class AppServiceProvider extends ServiceProvider {

    public function register() {
        if($this->app->runningUnitTests()) {
           $this->app->bind('Random', 'TestableRandom');
        } else {
           $this->app->bind('Random', 'RealRandom');
        }
    }
}

Second option is to use a mock in your tests 第二个选项是在测试中使用模拟

public function test_my_controller () {
    // Create a mock of the Random Interface
    $mock = Mockery::mock(RandomInterface::class);

    // Set our expectation for the methods that should be called
    // and what is supposed to be returned
    $mock->shouldReceive('someMethodName')->once()->andReturn('SomeNonRandomString');

    // Tell laravel to use our mock when someone tries to resolve
    // an instance of our interface
    $this->app->instance(RandomInterface::class, $mock);

    $this->post('/api/v1/do_things', ['email' => $this->email])
         ->seeInDatabase('things', [
             'email' => $this->email, 
             'random' => 'SomeNonRandomString',
         ]);
}

If you decide to go with the mock route. 如果你决定采用模拟路线。 Be sure to checkout the mockery documentation: 务必查看嘲弄文档:

http://docs.mockery.io/en/latest/reference/expectations.html http://docs.mockery.io/en/latest/reference/expectations.html

From laracasts来自 laracasts

class ApiClientTest extends TestCase
{
    use HttpMockTrait;

    private $apiClient;

    public function setUp()
    {
        parent::setUp();
        $this->setUpHttpMock();

        $this->app->bind(ApiConfigurationInterface::class, FakeApiConfiguration::class);

        $this->apiClient = $this->app->make(ApiClient::class);
    }

    /** @test */
    public function example()
    {
        dd($this->apiClient);
    }
}

results结果

App\ApiClient^ {#355
  -apiConfiguration: Tests\FakeApiConfiguration^ {#356}
}

https://laracasts.com/discuss/channels/code-review/laravel-58-interface-binding-while-running-tests?page=1&replyId=581880 https://laracasts.com/discuss/channels/code-review/laravel-58-interface-binding-while-running-tests?page=1&replyId=581880

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM