30 Days of Automated Testing:Using PHPUnit【D17】

Mocking (Part 2)

WilliamP
3 min readFeb 8, 2023

Let’s review previous Mocking introduction!

Mocking Introduction Retrospect

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

In the previous Mocking experience, we used four important Mocking functions. Let’s do a simple introduction to them!

Mocking Functions Explanation

  • shouldReceive(): When we expect a certain function in a specified class to be called during testing and want to mock the behavior of the function, we use shouldReceive() to capture this call behavior and then mock the subsequent behavior of the function.
  • with(): This function usually follows shouldReceive() and is used to capture function calls with a certain input argument combination and then mock the subsequent behavior of the function.
  • once(): This function usually follows with() and is used to capture function calls that are only made once and then mock the subsequent behavior of the function.
  • andReturn(): This function usually follows with() or once and is used to mock the response value of the function.

Testing Cases

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

In this case, we mocked the UserRepository class, captured a one-time call of the getUserById() function, with an argument of 1, and then mocked its response as 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);

In this case, we again mocked the UserRepository class, and captured the one-time call of the getUserById() function, with a call parameter of 1. Then, we mocked its response to be the test data $usercreated earlier. At the same time, we also mocked the PostRepository class, and captured the one-time call of the getPostsByUserId() function, with a call parameter of 1. Then, we mocked its response to be an empty array [].

The above is today’s review, hoping to give everyone a better understanding of the practicality of Mocking.

It is worth mentioning that these four functions are also considered as Assertion functions, so the number of test assertions displayed after the tests are run includes the number of times these functions were executed.

Next, we will continue to explore Mocking.

If you liked this article or found it helpful, feel free to give it some claps and follow the author!

Reference

Articles of This Series

--

--