PHP Laravel 使用模拟测试

PHP Laravel tests using mocking

我正在为我的存储库编写测试,我有一个 page() 方法,它接受一个 PaginationQuery 对象,该对象由特定模型的限制、方向和光标组成,returns 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);
}

我需要在这里完成几件事:

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

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

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

这导致永远不会使用分页器的模拟,因为创建了新对象。

您可以快速验证这一点,如果您使用 Paginator return 的 paginate 方法,例如 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 模型中您 return [=28],您也会得到 [] 作为结果=]

编辑

在评论中您询问了如何注入 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);
}

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

现在让我们回到您的测试。

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

$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 方法时传递分页器对象。