PHPUnit 自動化測試大作戰【CH17】

閱讀時間約 10 分鐘

今天讓我們回顧一下前一天的 Mocking 初體驗吧!

Mocking 初體驗回顧

  • app/Repositories/UserRepository.php
<?php

namespace App\Repositories;

use App\Models\User;

class UserRepository{

protected $model;

public function __construct(User $model) {

$this->model = $model;

} public function getUserById($userId) {

return $this->model::find($userId);

}}
  • app/Repositories/PostRepository.php
<?php

namespace App\Repositories;

class PostRepository{

protected $model;

}
  • app/Services/UserService.php
<?php

namespace App\Services;

use App\Repositories\PostRepository;

use App\Repositories\UserRepository;

class UserService{

private $userRepository;

private $postRepository;

public function __construct( PostRepository $postRepository, UserRepository $userRepository ) {

$this->postRepository = $postRepository;

$this->userRepository = $userRepository;

} public function getUserData(int $userId) {

$user = $this->userRepository->getUserById($userId);

if (empty($user)) {

return [];

} $user->posts = $this->postRepository->getPostsByUserId($userId);

return $user;

}}
  • tests/Feature/UserServiceTest.php
<?php

namespace Tests\Feature;

use App\Models\User;

use App\Repositories\PostRepository;

use App\Repositories\UserRepository;

use App\Services\UserService;

use Tests\TestCase;

class UserServiceTest extends TestCase{

public function testGetUserDataWhenUserNotFound() {

$this->mock(UserRepository::class, function ($mock) {

$mock->shouldReceive('getUserById')

->with(1)

->once()

->andReturn(null);

}); $service = app(UserService::class);

$user = $service->getUserData(1);

$this->assertEmpty($user);

} public function testGetUserData() {

$user = User::factory()->make();

$this->mock(UserRepository::class, function ($mock) use ($user) {

$mock->shouldReceive('getUserById')

->with(1)

->once()

->andReturn($user);

}); $this->mock(PostRepository::class, function ($mock) {

$mock->shouldReceive('getPostsByUserId')

->with(1)

->once()

->andReturn([]);

}); $service = app(UserService::class);

$user = $service->getUserData(1);

$this->assertNotEmpty($user);

$this->assertNotNull($user->posts);

}}

在前一天的 Mocking 初體驗中,我們有用到4個重要的 Mocking 函數,以下就針對它們做個簡單介紹吧!

Mocking 函數說明

  • shouldReceive():當我們預期指定類別的某個函數,會在測試過程被呼叫,且希望 Mock 此函數的行為時,我們就會用到 shouldReceive()來補捉這個呼叫行為,並且接著 Mock 該函數後續的行為。
  • with():此函數通常會接在shouldReceive()之後,用以補捉符合某種輸入參數組合的函數呼叫,並接著 Mock 該函數後續的行為。
  • once():此函數通常會接在with()之後,用以補捉只呼叫一次的函數呼叫,並接著 Mock 該函數後續的行為。
  • andReturn():此函數通常會接在with()once之後,用以 Mock 函數的回應值。

測試案例導覽

  • testGetUserDataWhenUserNotFound()
$this->mock(UserRepository::class, function ($mock) {

$mock->shouldReceive('getUserById')

->with(1)

->once() ->andReturn(null);});$service = app(UserService::class);

$user = $service->getUserData(1);

$this->assertEmpty($user);

在此案例中,我們 Mock 了 UserRepository 這個類別,並且補捉 getUserById() 的 1次性函數呼叫,且此呼叫附帶的函數呼叫參數是 1,接著我們 Mock 它的回應為 null

  • testGetUserData()
 $user = User::factory()->make();

$this->mock(UserRepository::class, function ($mock) use ($user) {

$mock->shouldReceive('getUserById')

->with(1)

->once() ->andReturn($user);

});$this->mock(PostRepository::class, function ($mock) {

$mock->shouldReceive('getPostsByUserId')

->with(1)

->once() ->andReturn([]);});$service = app(UserService::class);

$user = $service->getUserData(1);

$this->assertNotEmpty($user);

$this->assertNotNull($user->posts);

在此案例中,我們同樣 Mock 了 UserRepository 這個類別,並且補捉 getUserById() 的 1次性函數呼叫,且此呼叫附帶的函數呼叫參數是 1,接著我們 Mock 它的回應為前面所建立的測試資料 $user。同時我們還 Mock 了 PostRepository 這個類別,並且補捉 getPostsByUserId() 的 1次性函數呼叫,且此呼叫附帶的函數呼叫參數是 1,接著我們 Mock 它的回應為空陣列 []

以上就是今天的回顧,希望有讓大家更了解 Mocking 的實際作法。

另外值得一題的是,這4個函數也算是一種 Assertion 函數,因此在跑完測試後,顯示的測試 Assertion 數,是有包含這幾個函數被執行的次數的。

下一篇繼續來探討 Mocking。

如果您喜歡這篇文章,歡迎加入追蹤以接收新文章通知 😄

參考資料

本系列文章目錄

avatar-img
8會員
279內容數
歡迎來到 WilliamP 的沙龍天地,在這裡將與各位讀者探討各種主題,包刮高中數學題庫、PHP開發經驗、LINE聊天機器人開發經驗、書摘筆記等,歡迎交流!
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
WilliamP的沙龍 的其他內容
今天我們來聊聊「Mocking」吧! 何為 Mocking & 為何 Mocking 所謂的 Mocking,是指用各種方式來模擬它原本的行為與功能,藉此將我們要測試的對象,與其相依的外部服務「隔離」。簡單來說,就是做一個外部服務的「仿冒品」。這裡的外部服務,可以是其他類別函數、外部API、檔案
今天讓我們來看看播種器吧! 什麼是播種器 播種器 (Seeder) 是 Laravel 提供的一個批次建立測試資料的功能,可以讓我們將建立測試資料的邏輯,統一寫在一個播種器類別中,方便我們重複調用以建立否些特定資料。 播種器實例 建立播種器指令 php artisan make:seede
今天要來為大家介紹幾個,在撰寫測試程式碼時可以利用的特殊函數。 setUp() & tearDown() setUp():我們可以在這個函數中,撰寫想要在每個測試案例函數執行前預執行的邏輯。 tearDown():我們可以在這個函數中,撰寫想要在每個測試案例函數執行後預執行的邏輯。 範例:
這一篇讓我們看看幾個重要的 PHPUnit @ Annotation 吧! 所謂的 PHPUnit @ Annotation,是指在測試案例函數前的 PHP Doc 區塊,PHPUnit 提供開發者引用的 @Annotation。PHPUnit 提供的 @ Annotation 大約有 20+ 個
在之前的文章中,我們演練了許多測試方式,不過不知道大家有沒有發現,我們測試的大多是「正向」情況,「反向」的情況反而沒有測試到,也就是例外情況。 例外情況也可以測試嗎?當然可以! 本篇文章會為大家介紹如何「成功地測試失敗」。 例外測試函數 $this->expectException() 函
指令在現代 Laravel Web Applications 中,也是一個相當常見的應用,而 Laravel 也為此準備許多方便實現測試的函數,以下就來為大家介紹: artisan() 函數簽名: artisan($command, $parameters = []) 函數說明:這應該是指令測
今天我們來聊聊「Mocking」吧! 何為 Mocking & 為何 Mocking 所謂的 Mocking,是指用各種方式來模擬它原本的行為與功能,藉此將我們要測試的對象,與其相依的外部服務「隔離」。簡單來說,就是做一個外部服務的「仿冒品」。這裡的外部服務,可以是其他類別函數、外部API、檔案
今天讓我們來看看播種器吧! 什麼是播種器 播種器 (Seeder) 是 Laravel 提供的一個批次建立測試資料的功能,可以讓我們將建立測試資料的邏輯,統一寫在一個播種器類別中,方便我們重複調用以建立否些特定資料。 播種器實例 建立播種器指令 php artisan make:seede
今天要來為大家介紹幾個,在撰寫測試程式碼時可以利用的特殊函數。 setUp() & tearDown() setUp():我們可以在這個函數中,撰寫想要在每個測試案例函數執行前預執行的邏輯。 tearDown():我們可以在這個函數中,撰寫想要在每個測試案例函數執行後預執行的邏輯。 範例:
這一篇讓我們看看幾個重要的 PHPUnit @ Annotation 吧! 所謂的 PHPUnit @ Annotation,是指在測試案例函數前的 PHP Doc 區塊,PHPUnit 提供開發者引用的 @Annotation。PHPUnit 提供的 @ Annotation 大約有 20+ 個
在之前的文章中,我們演練了許多測試方式,不過不知道大家有沒有發現,我們測試的大多是「正向」情況,「反向」的情況反而沒有測試到,也就是例外情況。 例外情況也可以測試嗎?當然可以! 本篇文章會為大家介紹如何「成功地測試失敗」。 例外測試函數 $this->expectException() 函
指令在現代 Laravel Web Applications 中,也是一個相當常見的應用,而 Laravel 也為此準備許多方便實現測試的函數,以下就來為大家介紹: artisan() 函數簽名: artisan($command, $parameters = []) 函數說明:這應該是指令測