I am using phpunit and want to write unit tests for a class, which uses another class inside (instantiates object of it).
This other class in not passed via constructor or method argument.
For example in javascript you can mock the module, is something similar in phpunit?
The code is as below:
<?php
use SomeLibraryResourceSms;
final class SmsSender
{
public function __construct(private array $config) {}
public function send(string $phone, string $content): void
{
$sms = new Sms();
$sms->sendToOne($phone, $content, $this->config['sender']);
}
}
$sms = new Sms()
Is there a way to mock globally for my unit test the Sms
class then stub methods and assert that they are called with the correct arguments
my unit test so far
<?php declare(strict_types = 1);
namespace TestsUnitAppServicesSmsSender;
use AppServicesSmsSender;
use TestsHelpersTestCase;
class SmsSenderTest extends TestCase
{
private array $config;
private SmsSender $service;
public function setUp(): void
{
$this->config = [
'apiKey' => 'mock-api-key',
'apiSecret' => 'mock-api-secret',
'sender' => 'my-company-name'
];
$this->service = new SmsSender(
$this->sms->reveal(),
);
}
public function testSend(): void
{
// how to mock the SomeLibraryResourceSms ?
$this->service->send('+3344334', 'dfddfdfdf');
}
}
UPDATE:
I used one of the provided answers here and it works fine, re-declaring the class in my unit test file and using a custom mock.
**namespace SomeLibraryResourceSms;
class Sms {
public static array $calls = [];
public function sendToOne(string $phone, string $content, string $sender): void {
if (!array_key_exists('sendToOne', self::$calls)) {
self::$calls['sendToOne'] = [];
}
self::$calls['sendToOne'][] = func_get_args();
}
}
then using the custom mock asserted like
self::assertEquals([$phoneNumber, $content, $this->config['sender']], Sms::$calls['sendToOne'][0]);
3
Answers
Thanks to @MadCatERZ answer I managed to have my unit test working by re-declaring the Sms class inside the unit test and using a custom mock. It is very hard to stub yourself though, easy to see the calls and arguments only..
my new unit test file looks like
Try to add an Sms-Stub to your Unit test
As your script runs in namespace TestsUnitAppServicesSmsSender, the PHP-Interpreter will first take a look under this Namespace for the Sms-Class
I also implemented successfully one alternative solution proposed by @m-eriksson
It involves using the
Factory
pattern.As I did not want to have logic inside the DI container and slow down all calls with runtime code there, I used a factory as second argument of the
SmsSender
Then mocked the response.
Then in the unit tests had more flexibility
and could mock the response of smsFactory to use a mock and assert all calls and mock the responses.