Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Test | Mock object in Laravel Artisan Command

I want to test my Laravel Artisan command. So I need to mock an object and stubs this mocked object methods. In my test, I cannot use the real SFTP environment.

This is the handle() of my command:

public function handle()
{
   $sftp = new SFTP('my.sftpenv.com');
   $sftp->login('foo', 'bar');
}

I want to mock the SFTP in my test:

$sftp = $this->createMock(SFTP::class);
$sftp->expects($this->any())->method('login')->with('foo', 'bar');
$this->artisan('import:foo');

Running the test results in a Cannot connect to ...:22, which comes from the original login method of SFTP. So the mock/stub does not take effect.

So my question is: how can I mock an object in a Laravel Artisan command test?

like image 969
schellingerht Avatar asked Oct 16 '22 13:10

schellingerht


1 Answers

I think what @Mesuti means is that if you bind your SFTP object to your service container you would be able to swap it out with a mock object when running your test.

You could bind it like this (either inside your app/Providers/AppServiceProvider.php or a new service provider):

$this->app->singleton(SFTP::class, function ($app) {
            return new SFTP('my.sftpenv.com');
        });

You could then resolve the object in your command's handler (e.g. $sftp = resolve('SFTP');) and then mock it inside your test like this:

$this->mock(SFTP::class, function ($mock) {
    $mock->expects()->login('foo', 'bar')->andReturn('whatever you want it to return');
});

Just a note for future readers that service you are mocking should be resolved in the handle method of the command and not in the __construct method like you would often do in other circumstances. It seems like the artisan commands are resolved before the tests are run so if you resolve the service in the constructor of the command, it wouldn't resolve to the mocked instance.

like image 181
D Malan Avatar answered Nov 02 '22 09:11

D Malan