测试克隆模拟参数的方法

Posted

技术标签:

【中文标题】测试克隆模拟参数的方法【英文标题】:Testing a method that clones a mocked parameter 【发布时间】:2018-01-19 07:10:08 【问题描述】:

我有一个类 getPaginatedQuery,它的第一步会立即克隆它的一个参数:

public function getPaginatedQuery(Builder $query, $limit = null, $offset = null)

    $constrainedQuery = clone $query;
    ...

Builder 对象的克隆是通过其__clone 魔术方法完成的。

我正在尝试通过为$query 传递一个模拟的Builder 实例来对该方法进行单元测试:

$query = m::mock('Illuminate\Database\Eloquent\Builder');
$relation->getPaginatedQuery($query, 2, 1);

我的测试因致命错误而失败:__clone method called on non-object。我尝试在我的 $query 对象上定义对 __clone 的期望,但我得到了同样的错误:

$query = m::mock('Illuminate\Database\Eloquent\Builder');        
$clonedQuery = m::mock('Illuminate\Database\Eloquent\Builder');
$query->shouldReceive('__clone')->andReturn($clonedQuery);

$relation->getPaginatedQuery($query, 2, 1);

我在这里做错了什么?

【问题讨论】:

【参考方案1】:

Eloquent\Builder 实际上包含(作为成员)Query\Builder 的一个实例,其神奇的__clone 方法在这个底层Query\Builder 对象上调用clone

/**
 * Force a clone of the underlying query builder when cloning.
 *
 * @return void
 */
public function __clone()

    $this->query = clone $this->query;

由于您在模拟 Eloquent\Builder,它实际上并没有底层的 $this->query 成员,因为它将在 Eloquent\Builder 的构造函数中设置,而该构造函数永远不会在完全模拟的对象中被调用。

要解决这个问题,您需要创建一个 Eloquent\Builder 的部分模拟,并使用 Query\Builder 的模拟实例告诉 run its real constructor:

$baseQuery = m::mock('Illuminate\Database\Query\Builder');
$query = m::mock('Illuminate\Database\Eloquent\Builder', [$baseQuery])->makePartial();

$relation->getPaginatedQuery($query, 2, 1);

现在,当在 getPaginatedQuery() 中调用 clone $query 时,模拟的 Eloquent\Builder 实例将能够在 模拟的 Query\Builder 实例上调用 clone

【讨论】:

以上是关于测试克隆模拟参数的方法的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 IOS 的 XCTest 单元测试用例中模拟方法(带参数)调用

模拟 - 测试是否在不指定参数的情况下调用方法

Mockito单元测试

使用 OCMockito 测试发送到模拟协议的参数

Fiddler模拟接口返回进行测试

为 Junit 测试克隆现有数据库的最佳方法?