我正在编写一个单元测试来过滤掉模型中的无效关系。目标是仅保留模型中实际存在的关系。但是,我的测试失败了,因为过滤后的关系集合返回为空,即使我已经模拟了其中一个关系
validRelation
返回有效的 HasMany 关系。
其他背景:
我正在使用 Laravel 和 Mockery 进行单元测试。
filterRestrictedRelations
方法受到保护,因此我通过反射调用它。
我尝试将方法设置为
public
并省略反射部分,但集合仍然是空的 - 看起来问题在于 Mockery 如何处理 shouldReceive('validRelation')
什么可能导致此行为,以及如何正确测试有效关系是否未被过滤掉?
protected function filterRestrictedRelations(): void
{
$this->restrictedRelations = $this->restrictedRelations->filter(function ($relation) {
$exists = method_exists($this->model, $relation);
dump("Checking relation: $relation, exists: " . ($exists ? 'true' : 'false'));
return $exists;
});
}
这是我测试的相关部分:
public function setUp(): void
{
parent::setUp();
$this->model = \Mockery::mock(AnAbstractClass::class)->makePartial();
$this->modelRelation = \Mockery::mock(new ModelRelation($this->model));
$this->reflectionClass = new ReflectionClass($this->modelRelation);
}
public function it_filters_out_relations_that_do_not_exist_in_model(): void
{
$relations = ['validRelation', 'invalidRelation1', 'invalidRelation2'];
// Mocking model to have the 'validRelation' method returning a HasMany relation
$this->model->shouldReceive('validRelation')
->andReturn(\Mockery::mock(HasMany::class));
$this->modelRelation = new ModelRelation($this->model);
$this->modelRelation->setRestrictedRelations($relations);
$this->reflectionClass = new ReflectionClass($this->modelRelation);
// Invoke the filterRestrictedRelations method
$this->reflectionClass->getMethod('filterRestrictedRelations')
->invoke($this->modelRelation);
$filteredRelations = $this->reflectionClass->getMethod('getRestrictedRelations')->invoke($this->modelRelation);
// Check that invalidRelation1 remains, while others are filtered out
$this->assertEquals(['validRelation'], $filteredRelations->all());
}
当使用 Mockery 模拟现有类时,
method_exists()
函数将为原始类中实际存在的任何方法返回 true,但对于不存在的方法,它将返回 false,即使您使用 shouldReceive()
。
发生这种情况是因为
shouldReceive()
没有以常规方式创建方法;相反,它利用 __call()
魔术方法进行方法重载。
Mockery 提供了一种方法来验证模拟是否需要特定的方法调用。您可以确定模拟是否对该特定方法设置了任何期望,而不是使用 method_exists()。
protected function filterRestrictedRelations(bool $mocking = false): void
{
$this->restrictedRelations = $this->restrictedRelations->filter(function ($relation) {
return method_exists($this->model, $relation) || ($mocking && $this->model->mockery_getExpectationsFor($relation));
});
}