phpunit 测试中的 Lumen IoC 绑定分辨率参差不齐

Lumen IoC binding resolution spotty within phpunit tests

我 运行 遇到了 Lumen v5.0.10 的问题,这让我束手无策。我正在设计一个主要使用 TDD 和捆绑 phpunit 扩展的应用程序。 "App\Contracts\SubscriberInteractionInterface" 我基本上得到了 BindingResolutionException。这是目录 App\Contracts 中的一个接口,它在 App\Services 中有一个实现,它在 [=15] 中注册=] 像这样:

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() 的顶部 returns true。测试恰好是这样排序的,即首先使用 OwnerInteractionInterface 运行s 的路径可以很好地解析,然后另一个测试失败并显示 BindingResolutionException。但是,如果我省略其他测试而只 运行 那个测试,那么一切都会顺利进行。测试在不同的文件中,我做的唯一设置是为第三方 API 绑定一个模拟,以代替与显示的完全不同的绑定,并且 none 该代码触及这些 类.这是在 setUp() 函数中完成的,我确保在其中调用 parent::setUp()

这是怎么回事?会不会是绑定一个具体实例会从 IoC 擦除非具体绑定?还是默认设置允许某些影响从一项测试转移到另一项测试?

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

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

您可以使用 bindIf 方法代替 bind。容器会检查抽象是否已经绑定。如果没有,它将绑定您的摘要,反之亦然。您可以阅读 api here.

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

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

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

这就是说,App facade 用来解析你的对象的 Laravel\Lumen\Application 实例与当前实例 相同由 setUp() 方法创建。我认为这个实例是旧实例,所有绑定都已被 tearDown() 方法中的 $this->app->flush() 调用清除,因此它无法解析第一个 [ 之后的任何测试中的任何自定义绑定=20=]打电话。

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