Mocking

需求: 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 體驗: 外掛程式