lundi 2 juillet 2018

How to write a PHPUnit test for Laravel Artisan command that calls an external API without calling that API physically?

A brief introduction to give you an idea of what I want to achieve…

I have a Laravel 5.6 artisan command that calls an external API with a simple GET request to get some data. I want to test this without actually calling any API physically. (I want to be offline and still have test test green).

Now a brief explanation of how the logic is arranged.

1) This artisan command (php artisan export:data) has its own constructor (__construct) where I inject bunch of stuff. One of the things I inject is the ExportApiService class. Easy.

public function __construct(
    ExportApiService $exportApiService,
) {
    parent::__construct();

    $this->exportApiService = $exportApiService;
}

2) Now ExportApiService extends abstract class AbstractApiService. Inside this abstract class I made a constructor where I simply inject GuzzleHttp\Client in a $client property so that in ExportApiService I can call API by using $this->client. Easy.

3) So my ExportApiService has a method called exportData. Trivial. Works.

public function exportData(array $args = []): Collection
{
   $response = $this->client->request('GET', 'https://blahblah.blah');

   return collect(json_decode($response->getBody()->getContents(), true));
}

4) So finally in my artisan command (that I introduced in point no. 1) I call:

public function handle()
{
    $exportedData = $this->exportApiService->exportData($this->args);

    $this->info('Exported ' . count($exportedData) . ' records');
}

I want to test if the output matches my expectations without calling that external API at all. I want to mock it somehow…

public function testExportSuccessful()
{
    $this->console->call('export:data', [$some_arguments]);

    $output = $this->console->output();

    $this->assertContains('Exported 123 records', $output); // this does not work :(
}

Now my exact question - how can I force Laravel / PHPUnit to return a fixed value every time the artisan command will call exportData()?

What did you try?

I tried creating a setUp() method with the following code:

public function setUp()
{
    parent::setUp();

    $mock = $this->createMock(ExportApiService::class);

    $mock->method('exportData')->willReturn(123); // give me a dummy integer each time exportData is being called so that I can use is in the assert at the end
}

But this does not work at all however it makes sense to me.

Thank you for any help.



via Chebli Mohamed

Aucun commentaire:

Enregistrer un commentaire