0

我遇到了 Lumen v5.0.10 的问题,让我束手无策。我正在设计一个应用程序,主要使用带有捆绑phpunit扩展的 TDD。我基本上得到了BindingResolutionException“App\Contracts\SubscriberInteractionInterface”。这是目录App\Contracts中的一个接口,它在App\Services中有一个实现,注册AppServiceProvider如下:

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {

        // Owner manager
        $this->app->singleton(
            'App\Contracts\OwnerInteractionInterface',
            'App\Services\OwnerManager'
        );

        // Subscriber manager
        $this->app->singleton(
            'App\Contracts\SubscriberInteractionInterface', 
            'App\Services\SubscriberManager'
        );

//      dd($this->app->bound('App\Contracts\SubscriberInteractionInterface'));
    }
}

我的沮丧是,如果我取消注释函数中的最后一行,那么它表明它App\Contracts\SubscriberInteractionInterface已被绑定(因此可能会被解决)。

然后我有一个实际上看起来像这样的控制器

class MyController extends Controller {

    public function __construct(LoggerInterface $log)
    {
        $this->log = $log;
    } 


    public function index(Request $request)
    {
           if (/* Seems to come from owner */)
           {
               $owners = App::make('App\Contracts\OwnerInteractionInterface');
               return $owners->process($request);
           }

           if (/* Seems to come from subscriber */)
           {
               $subscribers = App::make('App\Contracts\SubscriberInteractionInterface');
               return $subscribers->process($request);
           }
    }
}

我以这种方式使用它们是因为我只希望实例化相关的一个(如果我对它们进行类型提示,则不会两者都发生),并且它们在它们的构造函数中也都有类型提示的依赖项。

问题是需要的测试路线OwnerInteractionInterface运行得很好,但需要的测试路线SubscriberInteractionInterface却没有。实现和接口非常相似,正如我之前展示的,它们都是同时注册的,我可以确认这SubscriberInteractionInterface是绑定的。事实上,如果我把这条线:

dd(App::bound('App\Contracts\SubscriberInteractionInterface'));

index()它的顶部返回true。测试恰好是有序的,这样使用的路径OwnerInteractionInterface首先运行并且可以很好地解决,然后另一个测试失败并显示BindingResolutionException. 但是,如果我省略其他测试并只运行那个测试,那么一切都会顺利进行。测试在不同的文件中,我做的唯一设置是为第三方 API 绑定一个模拟,而不是与所示的完全不同的绑定,并且没有任何代码涉及这些类。这是在一个setUp()函数内完成的,我确保parent::setUp()在它内调用。

这里发生了什么?是不是绑定一个具体实例会擦除非具体绑定IoC?还是默认设置允许一些影响从一个测试转移到另一个测试?

我知道我有一个解决方法,但是从不运行完整的测试套件的限制很烦人。如果我直接使用实例而不是从其界面解析它,那么测试似乎会更容易。

另外,有没有人知道检查IoC可解析绑定的方法?

4

2 回答 2

1

在进一步尝试调试后,我发现如果您使用app(...)代替,App::make(...)则不会出现问题。我在课堂上打了一个电话eval(\Psy\sh()),发现经过几次测试后,您得到以下结果:tearDownTestCase

>>> app()->bound('App\Contracts\OwnerInteractionInterface')
=> true
>>> App::bound('App\Contracts\OwnerInteractionInterface')
=> false
>>> App::getFacadeRoot() == app()           
=> false 

这就是说,Laravel\Lumen\Application外观App用于解析对象实例与方法创建的当前实例不同setUp()。我认为这个实例是旧实例,所有绑定都已通过方法$this->app->flush()中的调用清除,tearDown()因此它无法在第一次tearDown()调用之后的任何测试中解析任何自定义绑定。

我试图找出这个问题,但现在我必须用这个解决方法来结束这个项目。如果我找到实际原因,我会更新这个答案。

于 2015-06-23T13:48:54.347 回答
0

代替 use bind,您可以使用bindIf方法。容器将检查摘要是否已绑定。如果没有,它将绑定您的摘要,反之亦然。你可以在这里阅读 api 。

因此,如果您使用singleton,则可以使用bindIflike。

// Owner manager
$this->app->bindIf(
    'App\Contracts\OwnerInteractionInterface',
    'App\Services\OwnerManager',
    true
);

// Subscriber manager
$this->app->bindIf(
    'App\Contracts\SubscriberInteractionInterface', 
    'App\Services\SubscriberManager',
    true
);
于 2015-06-22T07:35:15.370 回答