iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0

Medium 清新閱讀版連結

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

(原本今天想與大家分享其他 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);
        }
    }
    

在前一天的 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 實例吧!

參考資料


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

尚未有邦友留言

立即登入留言