Mocking GuzzleHttp\Client 用于测试 Lumen 路由

Mocking GuzzleHttp\Client for testing Lumen route

我有一个使用以下两个文件的简单 Lumen 应用程序,在请求根路由 / 时,它会请求使用 GuzzleHttp\Client 获取 URL:

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() ] );
    }
}

但是,我想写一个测试,其中 $response->getStatusCode() 将 return 444,所以我写了下面的测试,试图模拟 getStatusCode() 中的方法 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();
    }
}

但是 运行 phpunit 失败了:

~/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.

说明mocking没有生效。我在这里错过了什么?

编辑-1

根据@sam 的评论 修改 MyController 构造函数后,我将测试修改如下:

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 ] );
}

您正在构造函数中实例化 Client,这意味着它不会从容器中解析出来。您的控制器应该接受 Client 作为构造函数中的参数,它将通过 Laravel.

从容器中解析出来
public function __construct(Client $client = null) {
    $this->client = $client ?: new Client;
}

如果没有提供,这只会创建一个新的 Client

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.