MMSs
MMSs

Reputation: 455

Mocking GuzzleHttp\Client for testing Lumen route

I have the a simple Lumen application using the following two files, that upon requesting the root route / it makes a request to get a URL using GuzzleHttp\Client:

routes/web.php

<?php

$router->get('/', ['uses' => 'MyController@index', 'as' => 'index']);

app/Http/Controllers/MyController

<?php

namespace App\Http\Controllers;

use GuzzleHttp\Client;
use Illuminate\Http\Request;

class MyController extends Controller {
    protected $client;

    public function __construct() {
        $this->client = new Client();
    }

    public function index( Request $request ) {
        $response = $this->client->request( 'get', 'https://www.google.com/' );
        return Response()->json( [ 'status' => $response->getStatusCode() ] );
    }
}

However, I want to write a test in which $response->getStatusCode() will return 444, so I wrote the following test, trying to mock getStatusCode() method in GuzzleHttp\Client:

<?php

use GuzzleHttp\Client;

class MyTest extends TestCase {
    protected $instance;

    public function testMyRoute() {
        $client = Mockery::mock( Client::class )
                         ->shouldReceive( 'getStatusCode' )
                         ->once()
                         ->andReturn( 444 );
        $this->app->instance( Client::class, $client );

        $this->json( 'get', '/' )->seeJson( [ 'status' => 444 ] );
    }

    public function tearDown() {
        parent::tearDown();
        Mockery::close();
    }
}

But running phpunit fails:

~/experiment/lumen-test/testing » phpunit                                                                                                                                 
PHPUnit 5.7.27 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 868 ms, Memory: 14.00MB

There was 1 failure:

1) MyTest::testMyRoute
Unable to find JSON fragment ["status":444] within [{"status":200}].
Failed asserting that false is true.

~/experiment/lumen-test/testing/vendor/laravel/lumen-framework/src/Testing/Concerns/MakesHttpRequests.php:288
~/experiment/lumen-test/testing/vendor/laravel/lumen-framework/src/Testing/Concerns/MakesHttpRequests.php:213
~/experiment/lumen-test/testing/tests/MyTest.php:15

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

That indicates that mocking did not take any effect. What am I missing here?

Edit -1

After modifying MyController constructor as per @sam's comment Mocking GuzzleHttp\Client for testing Lumen route, I modified the test to be as follows:

use GuzzleHttp\Psr7\Response;

-

public function testMyRoute() {
    $response = new Response(444);
    $client = Mockery::mock( Client::class )
                     ->makePartial()
                     ->shouldReceive( 'request' )
                     ->once()
                     ->andReturn( $response );
    $this->app->instance( Client::class, $client );

    $this->json( 'get', '/' )->seeJson( [ 'status' => 444 ] );
}

Upvotes: 1

Views: 2293

Answers (1)

sam
sam

Reputation: 5599

You're instantiating the Client in your constructor which means that it's not being resolved out of the container. Your controller should accept the Client as a parameter in the constructor, which will be resolved out of the container by Laravel.

public function __construct(Client $client = null) {
    $this->client = $client ?: new Client;
}

This will only create a new Client if one has not been provided.

https://laravel.com/docs/5.6/container#resolving

Automatic Injection

Alternatively, and importantly, you may "type-hint" the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, queue jobs, middleware, and more. In practice, this is how most of your objects should be resolved by the container.

Upvotes: 3

Related Questions