Laravel + Mockery InvalidCountException
Posted
技术标签:
【中文标题】Laravel + Mockery InvalidCountException【英文标题】: 【发布时间】:2019-04-21 00:10:42 【问题描述】:我正在尝试模拟 a class 以防止它不得不调用 3rd 方 api。但是在设置模拟时,它似乎并没有影响控制器的动作。我确实尝试通过手动创建Request
- 和OEmbedController
-classes 的实例来替换$this->postJson()
。 create()
-method 被调用,但我收到来自 Mockery 的错误,它不是。
我在这里做错了什么?
错误:
Mockery\Exception\InvalidCountException : 来自 Mockery_2_Embed_Embed 的方法 create() 应该被准确调用 1 次,但被调用 0 次。
测试:
class OEmbedTest extends TestCase
public function tearDown()
Mockery::close();
/**
* It can return an OEmbed object
* @test
*/
public function it_can_return_an_o_embed_object()
$url = 'https://www.youtube.com/watch?v=9hUIxyE2Ns8';
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
$response = $this->postJson(route('oembed', ['url' => $url]));
$response->assertSuccessful();
控制器:
public function __invoke(Request $request)
$info = Embed::create($request->url);
$providers = $info->getProviders();
$oembed = $providers['oembed'];
return response()
->json($oembed
->getBag()
->getAll());
【问题讨论】:
不需要测试嵌入包;已经是tested了,对吧? 是的,所以我的想法是模拟该类的响应。 提示:您可以将__invoke
方法重命名为handle
,它看起来会更好。您正在 (invoke) 方法中创建 Embed 的一个实例;没有模拟,那是无法测试的。我个人避免嘲笑任何东西并诉诸存根/假类等。我建议让 Laravel 在它到达控制器之前构建/创建/实例化 Embed 对象。这意味着创建一个服务提供者,它将负责为您实例化 Embed 对象,并可能将其放置在 $request
本身中。请添加有关您正在访问的路线的更多信息。
所以你基本上是说我应该依赖注入类?喜欢$this->app->bind()
。当然这是可能的,但如果没有它,它不应该是可能的吗?因为那时我需要一个接口等,这对于这个小测试来说感觉很麻烦。
你不需要接口,直接使用->bind(Embed\Embed::class, function($app) return Embed\Embed::create($app['request']->url); );
现在不知道create
被调用后会发生什么。但是在控制器中你现在可以使用$info = resolve(Embed\Embed::class)
;显然,您需要检查请求是否具有 url 参数等(在绑定闭包中执行此操作)。现在在测试setUp
你需要重新绑定它,更多信息请阅读***.com/questions/50262576/…
【参考方案1】:
您似乎以错误的方式嘲笑Embed
类。如果你使用 Laravel 门面方法 shouldReceive()
而不是创建类本身的 Mock,框架会为你将 mock 放入服务容器中:
Embed::shouldReceive('create')
->with($url)
->once();
而不是
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
另外请注意,如果您的测试代码传递给 mock 的参数与您通过 with($url)
学习到的 mock 不同,则 mock 认为自己未调用。但是无论如何调用未定义的方法,您都会收到另一个错误。
【讨论】:
Embed
类不是 Facade。它的命名空间是 Embed\Embed。
所以你想模拟一个静态方法?恐怕这行不通。
不知道不使用 Facades 就不能模拟静态方法。
这实际上是模拟库的限制,而不是 Laravel。 Facades 只提供对方法的静态访问;这并不意味着方法本身是静态的(事实上,据我所知,它们从来都不是)。因此,实现可测试性的最简单方法可能是围绕它构建自己的代理类......(不使用静态方法)。【参考方案2】:
我能够通过在我的测试中使用它来解决这个问题:
protected function setUp()
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
然后这样解决
$embed = resolve(Embed::class);
$embed = $embed->create($url);
【讨论】:
所以你创建了另一个类(FakeEmbed)来模拟? 是的,这是最简单的方法。以上是关于Laravel + Mockery InvalidCountException的主要内容,如果未能解决你的问题,请参考以下文章
这个模拟对象上不存在方法 - Laravel,Mockery
Laravel 5 - 使用 Mockery 模拟 Eloquent 模型