Reputation: 626
Still pretty new to testing and can't work this out. How do I prevent Spatie's package from actually running here. I just want to fake a response from it.
Example Controller:
<?php
namespace App\Http\Controllers;
use Spatie\SslCertificate\SslCertificate;
class SSLController extends Controller {
function getIsValid(){
$certificate = SslCertificate::createForHostName('https://example.url');
return $certificate->isValid();
}
}
Example Test:
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Spatie\SslCertificate\SslCertificate;
class SSLCheckPlugInTest extends TestCase {
public function it_returns_the_ssl_certs_status(){
$this->mock(SslCertificate::class,function($mock){
$mock->shouldReceive('createForHostName')->once();
});
$response = $this->get('/path-that-calls-the-controller-above');
}
}
This example code still triggers the actual call in the package (to example.url) and returns:
Mockery\Exception\InvalidCountException: Method createForHostName(<Any Arguments>) from Mockery_2_Spatie_SslCertificate_SslCertificate should be called
exactly 1 times but called 0 times.
I feel like I'm missing something really obvious.
edit
@bishop suggested writing a mock just for that one isValid method, but I can't work out how to do that.
Upvotes: 1
Views: 155
Reputation: 11414
The $this->mock()
method in a Laravel's tests cases is specifically meant for mocking an instance of an object in Laravel's service container. From the docs:
When mocking an object that is going to be injected into your application via Laravel's service container, you will need to bind your mocked
instance
into the container as an instance binding.
You can use Mockery directly instead to mock other classes or instances that won't be loaded into the service container.
In my opinion you should mock both the createForHostName
and isValid
methods. The purpose of your test would basically then be just to check that your controller method returns what it should given a known validity of a certificate.
You can first mock the SslCertificate
instance that you want to return from createForHostName
with something like this:
$certificateMock = Mockery::mock('overload:Spatie\SslCertificate\SslCertificate');
$certificateMock->shouldReceive('isValid')->once()->andReturn(True);
We're overloading the class above so that we can mock both instance methods and static methods of the class.
Then you can mock the createForHostName
method to return an instance of the mocked class from above:
$certificateMock->shouldReceive('createForHostName')->once()->andReturn($certificateMock);
Upvotes: 1
Reputation: 39389
It’ll be difficult to mock SslCertificate
because you’re using it statically.
Instead, you should inject it into the class (or method). That way, it’ll be resolved by Laravel’s service container, so you’ll be able to mock it.
class SSLController extends Controller
{
function getIsValid(SslCertificate $sslCertificate)
{
$certificate = $sslCertificate->createForHostName('https://example.url');
return $certificate->isValid();
}
}
class SSLCheckPlugInTest extends TestCase
{
public function it_returns_the_ssl_certs_status()
{
$this->mock(SslCertificate::class, function ($mock) {
$mock->shouldReceive('createForHostName')->once();
});
$response = $this->get('/path-that-calls-the-controller-above');
}
}
Now, when the controller is ran, it’ll check the container for SslCertificate
and find your mock and execute the expectations.
Upvotes: 1