在Laravel中,如何在测试时为服务容器提供另一种实现



我正在创建一个Laravel控制器,其中一个随机字符串生成器接口被注入到其中一个方法。然后在AppServiceProvider中注册一个实现。

控制器使用随机字符串作为输入,将数据保存到数据库。因为它是随机的,所以我不能像这样测试它(使用MakesHttpRequests):

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

因为我不知道'abc123'在使用实际的随机生成器时会是什么。所以我创建了Random接口的另一个实现,它总是返回'abc123',所以我可以对它进行断言。

问题是:我如何在测试时绑定到这个假生成器?我试着做

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

,但它仍然使用我在AppServiceProvider中注册的实际生成器。什么好主意吗?关于如何测试这样的东西,我是否完全走错了轨道?

谢谢!

您有两个选择:

使用条件来绑定实现:

class AppServiceProvider extends ServiceProvider {
    public function register() {
        if($this->app->runningUnitTests()) {
           $this->app->bind('Random', 'TestableRandom');
        } else {
           $this->app->bind('Random', 'RealRandom');
        }
    }
}
第二个选项是在测试中使用mock
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',
         ]);
}

如果您决定使用模拟路由。一定要签出mock文档:

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

From laracast

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);
    }
}
结果

AppApiClient^ {#355
  -apiConfiguration: TestsFakeApiConfiguration^ {#356}
}

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

最新更新