I have some class that creates user "integration" and check API credentials using external API:
class IntegrationService
{
public function create(array $params)
{
$api = new IntegrationApi();
if (!$api->checkCredentials($params['api_key'])) {
throw new \Exception('Invalid credentials');
}
// storing to DB
return 'ok'; // just for example
}
}
IntegrationApi class:
class IntegrationApi
{
public function checkCredentials(string $apiKey): bool
{
// some external api calls
return false; // just for example
}
}
I need to create unit tests for IntegrationService class. I'm trying to mock IntegrationApi class in my test before creating test integration, but my test fails with that exception...
class TestIntegrationService extends TestCase
{
public function test_create()
{
$service = new IntegrationService();
$this->mock(IntegrationApi::class, function (MockInterface $mock) {
$mock->shouldReceive('checkCredentials')->withArgs(['apiKey'])->once()->andReturnTrue();
});
$res = $service->create(['api_key' => '123']);
$this->assertEquals('ok', $res);
}
}
It seems that the IntegrationApi object was not mocked as expected, but I haven't any idea why. Have I applied object mocking correctly in this case?
You need to understand Dependency Injection and Service Container concepts.
For first, never ever use new
keyword in Laravel projects - use dependency injection through constructor:
class IntegrationService
{
private IntegrationApi $api;
public function __construct(IntegrationApi $api)
{
$this->api = $api;
}
public function create(array $params)
{
if (!$this->api->checkCredentials($params['api_key'])) {
throw new \Exception('Invalid credentials');
}
// storing to DB
return true; // never use magic strings. But in this case - void be preferred - throw exceptions on error and return nothing
}
}
test in this case be like
public function setUp()
{
$this->mockApi = Mockery::mock(IntegrationApi::class);
$this->service = new IntegrationService($this->mockApi);
}
public function testCreateOk()
{
$this->mockApi->shouldReceive('checkCredentials')->withArgs(['apiKey'])->once()->andReturnTrue();
$this->assertTrue($this->service->create(['apiKey']));
}
public function testCreateError()
{
$this->mockApi->shouldReceive('checkCredentials')->withArgs(['apiKey'])->once()->andReturnFalse();
$this->expectsException(Exception::class);
$this->service->create(['apiKey']);
}