模拟

要求: Mockery 1.0+

在测试应用程序时,您可能希望“模拟”特定类以防止它们在特定测试期间实际被调用。例如,如果您的应用程序与启动付款的 API 交互,您可能希望在本地“模拟”API 客户端以防止实际付款。

在开始之前,您需要安装一个模拟库。我们推荐 Mockery,但您可以自由选择任何适合您需求的其他库。

要开始使用 Mockery,请使用 Composer 包管理器将其引入。

1composer require mockery/mockery --dev

虽然 Mockery 的完整文档可以在 Mockery 网站 上找到,但本节将讨论模拟最常见的用例。

方法期望

模拟对象对于隔离正在测试的代码并模拟应用程序其他部分的特定行为或条件至关重要。在使用 Mockery::mock() 方法创建模拟后,我们可以通过调用 shouldReceive() 方法来指示我们期望某个特定方法被调用。

1use App\Repositories\BookRepository;
2use Mockery;
3 
4it('may buy a book', function () {
5 $client = Mockery::mock(PaymentClient::class);
6 $client->shouldReceive('post');
7 
8 $books = new BookRepository($client);
9 $books->buy(); // The API is not actually invoked since `$client->post()` has been mocked...
10});

可以使用上面显示的相同语法模拟多个方法调用。

1$client->shouldReceive('post');
2$client->shouldReceive('delete');

参数期望

为了使我们对方法的期望更具体,我们可以使用约束来限制方法调用的预期参数列表。这可以通过使用 with() 方法来完成,如下例所示。

1$client->shouldReceive('post')
2 ->with($firstArgument, $secondArgument);

为了提高参数匹配的灵活性,Mockery 提供了内置的匹配器类,这些类可以用来代替特定值。例如,我们可以使用 Mockery::any() 来匹配任何参数,而不是使用特定值。

1$client->shouldReceive('post')
2 ->with($firstArgument, Mockery::any());

需要注意的是,使用 shouldReceive()with() 定义的期望仅在方法使用您期望的完全相同的参数调用时才适用。否则,Mockery 将抛出异常。

1$client->shouldReceive('post')->with(1);
2 
3$client->post(2); // fails, throws a `NoMatchingExpectationException`

在某些情况下,使用闭包来同时匹配所有传递的参数可能比依赖于每个单独参数的内置匹配器更合适。withArgs() 方法接受一个闭包,该闭包接收传递给预期方法调用的所有参数。因此,此期望仅适用于传递的参数导致闭包计算结果为真的方法调用。

1$client->shouldReceive('post')->withArgs(function ($arg) {
2 return $arg === 1;
3});
4 
5$client->post(1); // passes, matches the expectation
6$client->post(2); // fails, throws a `NoMatchingExpectationException`

返回值

在使用模拟对象时,我们可以使用 andReturn() 方法告诉 Mockery 从模拟方法中返回什么。

1$client->shouldReceive('post')->andReturn('post response');

我们可以通过将多个返回值传递给 andReturn() 方法来定义一系列返回值。

1$client->shouldReceive('post')->andReturn(1, 2);
2 
3$client->post(); // int(1)
4$client->post(); // int(2)

有时,我们可能需要根据传递给方法的参数来计算方法调用的返回值。这可以通过使用 andReturnUsing() 方法来实现,该方法接受一个或多个闭包。

1$mock->shouldReceive('post')
2 ->andReturnUsing(
3 fn () => 1,
4 fn () => 2,
5 );

此外,我们可以指示模拟方法抛出异常。

1$client->shouldReceive('post')->andThrow(new Exception);

方法调用“次数”期望

除了指定方法调用的预期参数和返回值外,我们还可以设置对特定方法应调用多少次的期望。

1$mock->shouldReceive('post')->once();
2$mock->shouldReceive('put')->twice();
3$mock->shouldReceive('delete')->times(3);
4// ...

要指定方法应调用的最少次数,我们可以使用 atLeast() 方法。

1$mock->shouldReceive('delete')->atLeast()->times(3);

Mockery 的 atMost() 方法允许我们指定方法可以调用的最大次数。

1$mock->shouldReceive('delete')->atMost()->times(3);

本节的主要目的是为您提供 Mockery(我们首选的模拟库)的介绍。但是,为了更全面地了解 Mockery,我们建议查看其 官方文档。接下来,让我们探索 Pest 的插件,并了解它们如何增强您的 Pest 体验:插件