Medium 清新閱讀版
:連結
今天讓我們回顧一下前一天的 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 函數,以下就針對它們做個簡單介紹吧!
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 實例吧!