iT邦幫忙

2022 iThome 鐵人賽

DAY 16
0

Medium 清新閱讀版連結

今天我們來聊聊「Mocking」吧!(話說30天已經過了一半了!)

何為 Mocking & 為何 Mocking

所謂的 Mocking,是指用各種方式來模擬它原本的行為與功能,藉此將我們要測試的對象,與其相依的外部服務「隔離」。簡單來說,就是做一個外部服務的「仿冒品」。這裡的外部服務,可以是其他類別函數、外部API、檔案系統存取介面等等。

那為什麼需要 Mocking呢?大家可以試想幾種情境:

  • 假如我們有一個API,它會呼叫外部服務A Service,如果某天A Service停機維修,而我們沒有做Mocking A Service,那我們所寫的測試程式是否就會隨之停擺或出錯?
  • 同樣的情況,假如我們要測試當A Service掛點時,我們的API仍然可以某種方式正常運作,在無法打電話給A Service廠商,請他們停機時,此時該如何測試以上行為?
  • 同樣的情況,假如 A Service 其實還沒上線,只知道預期中的介面與行為,此時我們該如何測試?

在以上的情況中,有一個很重要的共通點,那就是測試能否運行,都依賴於外部服務是否為我們所控制。為了破除這個依賴,Mocking 就由此而生了!

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);
        }
    }
    

    在以上測試程式碼中,我們撰寫了 2 個測試案例函數:

    • testGetUserDataWhenUserNotFound()

      在這個函數中,我們 Mock 了 UserRepository→getUserById() 這個函數的行為,因此即便我們沒有準備測試資料,仍然可以測試「UserRepository→getUserById(1) 的回應為 null 」的這個情境下,UserService→getUserData(1) 是否可正常運作。

    • testGetUserData()
      在這個函數中,這次我們 Mock 了 UserRepository→getUserById()PostRepository→getPostsByUserId()及這2個函數的行為,而這次我們甚至 Mock 了一個還沒實作的函數 PostRepository→getPostsByUserId()

以上便是今天的 Mocking 初體驗,是不是很有趣呢?

明天讓我們繼續探所更多關於 Mocking 的各種技巧吧!

參考資料


上一篇
Seeder:播種器
下一篇
Mocking(二)
系列文
自動化測試大作戰31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言