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=]打电话。
我试图找出问题所在,但现在我必须用这个解决方法来结束这个项目。如果我找到真正的原因,我会更新这个答案。
我 运行 遇到了 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=]打电话。
我试图找出问题所在,但现在我必须用这个解决方法来结束这个项目。如果我找到真正的原因,我会更新这个答案。