Introduction
Mocks are basically clones of your classes. They override your class while maintaining the same type and return type as the original class. Ensuring the correct return type during the mock process is essential; otherwise, errors will occur. Let's say we want to mock a class used inside our calculator.
How to Mock in PHPUnit
Following the previous code, add a new class called PrintCalculationService
:
<?php
declare(strict_types=1);
namespace App\Service;
class PrintCalculationService
{
function array(int $result): array
{
# a lot of business rules...
return [
'result' => $result
];
}
function print(int $result): string
{
# a lot of business rules...
return 'result: ' . $result;
}
}
setUp
and tearDown
These functions control the global state and execute before and after each test.
-
setUp
-> Executes actions before each test. -
tearDown
-> Executes actions after each test.
createMock
and createStub
These functions create a mock of your class. The key difference is that mocks provide more functionality than stubs. In other test frameworks, they have distinct purposes, but in PHPUnit, they are nearly identical.
-
createStub
-> Creates a stub based on the class. -
createMock
-> Creates a mock based on the class. -
->method('print')
-> Configures the method to return a specified value for testing purposes. -
->willReturn('result: 2')
-> Defines the return value to satisfy and pass the test.
There are many other configurations available. You can find more details in the PHPUnit Test Doubles Documentation.
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Service;
use App\Service\CalculatorService;
use App\Service\PrintCalculationService;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class CalculatorServiceTest extends TestCase
{
private MockObject|PrintCalculationService|null $printCalculationService = null;
protected function setUp(): void
{
$this->printCalculationService = $this->createMock(PrintCalculationService::class);
}
protected function tearDown(): void
{
$this->printCalculationService = null;
}
static function sumProvider() {
yield [10, 10, 20];
yield [1, 1, 2];
yield [2, 2, 4];
}
#[Test]
#[DataProvider('sumProvider')]
function testSum($x, $y, $expected) {
$service = new CalculatorService($this->printCalculationService);
$this->assertSame(
$service->sum($x, $y),
$expected
);
}
#[Test]
function testDivision() {
$service = new CalculatorService($this->printCalculationService);
$this->assertEquals(
$service->division(10, 10),
1
);
}
#[Test]
function testDivisionException() {
$this->expectException(\Exception::class);
$service = new CalculatorService($this->printCalculationService);
$service->division(0, 10);
}
#[Test]
function testSumPrint() {
$service = new CalculatorService($this->printCalculationService);
$result = $service->sum(1, 1);
$this->assertSame($result, 2);
$this->printCalculationService->method('array')->willReturn([
'result mock: 2'
]);
$print = $service->show($result, 'array');
$this->assertSame($print, ['result mock: 2']);
$this->printCalculationService->method('print')->willReturn('result mock: 2');
$print = $service->show($result, 'print');
$this->assertSame($print, 'result mock: 2');
}
}
Final Thoughts
This has been an insightful journey! I hope you enjoyed it. I aimed to be as concise and to the point as possible, making it easier to quickly access the information. As I mentioned, writing tests requires a different mindset. We need to think in reverse. Mocks are an excellent way to approach this concept since they allow us to manipulate results and test every possible path in a function.
Now I encourage you to go for more informations and best practives for your tests, this is just a ready to go and then you can already starting getting your code more senior
Additional Topics
- Code Coverage
- How to configure the PHPUnit XML file
Recommended Reading
- PHPUnit Attributes Documentation
- PHPUnit Fixture Documentation
- PHPUnit Code Coverage Documentation
- PHPUnit Extending Documentation
GitHub Repository: Leave a star!
What do you think? Was this useful? Leave a comment!
Top comments (1)
If you have any questions leave a comment!