【问题标题】:Lumen IoC binding resolution spotty within phpunit testsphpunit 测试中的流明 IoC 绑定分辨率参差不齐
【发布时间】:2015-06-21 23:18:43
【问题描述】:

我遇到了 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 的可解析绑定的方法吗?

【问题讨论】:

  • 似乎其他人在 Laravel 中遇到了与单元测试类似的问题,只是他们在 issue 中讨论的 Eloquent 事件中遇到了这些问题。
  • 这个问题似乎与 Lumen 如何在 tearDown()setUp()TestCase 方法中销毁和重新设置应用程序实例有关。第一次工作正常,但每隔一段时间就不太对劲了。在后续测试中也可以使用OwnerInteractionInterface 观察到该问题。目前的解决方法是一次只运行 一个 测试。

标签: php laravel-5 ioc-container lumen


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

    您可以使用bindIf 方法,而不是使用bind。容器将检查摘要是否已绑定。如果没有,它将绑定您的摘要,反之亦然。你可以阅读apihere

    所以如果你使用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
    );
    

    【讨论】:

    • 似乎没有骰子。我认为这基本上就是 singleton() 在幕后所做的。
    猜你喜欢
    • 2017-01-24
    • 2021-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-18
    • 1970-01-01
    相关资源
    最近更新 更多