使用模拟

时间:2016-04-12 15:37:32

标签: php laravel testing mocking mockery

我正在为我的存储库编写测试,并且我有一个page()方法,它接受一个PaginationQuery对象,该对象由特定模型的限制,方向和游标组成,并返回一个json paginator对象,我正在努力进行模拟,这是我的代码:

我的存储库代码:

   public function page(PaginationQuery $pagination)
    {
        $paginator = new Paginator($this->model);
        return $paginator->paginate($pagination->limit(), $pagination->direction(), $pagination->cursor());
    }

我的测试:

public function test_paging_with_mocking()
{
    // seed articles
    $seededArticles = [
        new Article(['title' => 'Article 1']),
        new Article(['title' => 'Article 2']),
        new Article(['title' => 'Article 3']),
        new Article(['title' => 'Article 4']),
        new Article(['title' => 'Article 5']),
        new Article(['title' => 'Article 6']),
    ];

    foreach ($seededArticles as $seededArticle) {
        $seededArticle->save();
    }

    $limit =  PaginationQuery::defaultLimit();
    $direction = PaginationQuery::defaultDirection();
    $cursor = PaginationQuery::defaultCursor();

    $pagination = new PaginationQuery($limit, $direction, $cursor);

    $mPaginator = M::mock(Paginator::class);
    $mPaginator->shouldReceive('paginate')->with($limit, $direction, $cursor)->andReturn([]);

    $receivedResult = $this->repo->page($pagination);
}

我需要在这里做几件事:

  • 在模拟paginator时测试page()方法,因为我不关心输出,我只需要声明限制,方向和游标值正确传递给paginate ()方法。

  • 我还需要在page()方法中注入paginator类,因为模拟类的new实例会在模拟这个类时导致错误

  • 在测试的这一行$mPaginator->shouldReceive('paginate')->with($limit, $direction, $cursor)->andReturn([]);我需要添加一次()但是这样做时我收到以下错误:

    应该叫

     正好1次,但被称为0次

1 个答案:

答案 0 :(得分:1)

我认为这里的问题是在page方法中使用以下方法创建新对象:

$paginator = new Paginator($this->model);

而不是以某种方式将其传递给存储库。

这导致从不使用Paginator的mock,因为创建了新对象。

您可以快速验证这一点,如果您paginate Paginator方法返回testtesttest并在测试结束时添加:

$this->assertEquals([], $receivedResult);

您将看到收到的结果将是testtesttest而不是[],尽管您使用andReturn([])进行设置,因为您不会将模拟对象传递到存储库。

使测试通过的最简单的解决方案是将page方法更改为:

public function page(PaginationQuery $pagination, Paginator $paginator)
{
    return $paginator->paginate($pagination->limit(), $pagination->direction(), $pagination->cursor());
}

现在整个测试结束如下:

$mPaginator->shouldReceive('paginate')->once()->with($limit, $direction, $cursor)->andReturn([]);

$receivedResult = $this->repo->page($pagination, $mPaginator);
$this->assertEquals([], $receivedResult);

将通过,因为您将模拟对象传递到存储库,现在如您所见,即使在[]模型中返回Paginator

,也会得到testtest

修改

在评论中,您询问了如何注入Paginator对象,因此可能有解决方案。

重写您的存储库以使用如下代码:

public function page(PaginationQuery $pagination)
{
    $paginator = $this->getPaginator();

    return $paginator->paginate($pagination->limit(),
        $pagination->direction(), $pagination->cursor());
}

protected function getPaginator()
{
    return new Paginator($this->model);
}

所以你现在正在创建额外的方法来创建paginator实例。

现在让我们回到你的考试。

在测试构造函数中的某处,您创建了存储库实例。让我们假设您这样做:

$this->repo = new \App\Repositories\ArticleRepository();

现在,我们希望将其更改为部分模拟:

$this->repo = M::mock("\\App\\Repositories\\ArticleRepository[getPaginator]")->shouldAllowMockingProtectedMethods();

所以我们告诉我们将为getPaginator方法创建模拟,我们使用shouldAllowMockingProtectedMethods因为我们不想模拟受保护的方法(getPaginator是受保护的方法)而且它不能默认情况下完成。

现在,最后一行测试应如下所示:

$mPaginator->shouldReceive('paginate')->once()->with($limit, $direction, $cursor)->andReturn([]);

// here we mock getPaginator method to return our mock $mPaginator
$this->repo->shouldReceive('getPaginator')->once()->andReturn($mPaginator);

$receivedResult = $this->repo->page($pagination);
$this->assertEquals([], $receivedResult);

所以当你看到我们现在达到了我们想要的效果时,我们不必在运行page方法时传递Paginator对象。