在phpunit测试中,流明IoC绑定解决方案参差不齐

时间:2015-06-21 23:18:44

标签: php laravel-5 ioc-container lumen

我遇到了Lumen v5.0.10的一个问题,让我在我的智慧结束时。我正在设计一个主要使用带有phpunit扩展的TDD的应用程序。我基本上为“App \ Contracts \ SubscriberInteractionInterface”获得BindingResolutionException。这是目录 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可解析绑定的方法?

2 个答案:

答案 0 :(得分:1)

在进一步尝试调试之后,我发现如果您使用app(...)代替App::make(...),那么问题就不会出现。我在tearDown课程的TestCase中进行了eval(\Psy\sh())调用,发现经过几次测试后您得到以下结果:

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

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

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

答案 1 :(得分:0)

您可以使用bindIf方法,而不是使用singleton。 Container将检查抽象是否已绑定。如果没有,它将绑定您的摘要,反之亦然。你可以阅读api This is m jsfiddle link

因此,如果您使用bindIf,则可以使用// Owner manager $this->app->bindIf( 'App\Contracts\OwnerInteractionInterface', 'App\Services\OwnerManager', true ); // Subscriber manager $this->app->bindIf( 'App\Contracts\SubscriberInteractionInterface', 'App\Services\SubscriberManager', true ); 之类的。

{{1}}