如何在Laravel功能測試中模擬一個使用GuzzleHttp客戶端向第三方API發出請求的情況?
P粉842215006
P粉842215006 2023-11-09 11:42:58

在 Laravel 專案(PHP 8.0 上的 Laravel 8)中,我有一個功能測試,其中測試了內部端點。端點有一個控制器呼叫服務上的方法。然後,服務嘗試呼叫第三方端點。我想模擬的就是這個第三方端點。目前的情況是這樣的:

內部端點功能測試

public function testStoreInternalEndpointSuccessful(): void
{
    // arrange, params & headers are not important in this problem
    $params = [];
    $headers = [];

    // act
    $response = $this->json('POST', '/v1/internal-endpoint', $params, $headers);

    // assert
    $response->assertResponseStatus(Response::HTTP_OK);
}

內部端點控制器

class InternalEndpointController extends Controller
{

    public function __construct(protected InternalService $internalService)
    {
    }

    public function store(Request $request): InternalResource
    {
        $data = $this.internalService->fetchExternalData();

        return new InternalResource($data); // etc.
    }
}

內部服務

use GuzzleHttpClientInterface;

class InternalService
{
    public function __construct(protected ClientInterface $client)
    {
    }
    
    public function fetchExternalData()
    {
        $response = $this->httpClient->request('GET', 'v1/external-data');
        $body = json_decode($response->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR);

        return $body;
    }
}

我看過Guzzle的文檔,但似乎MockHandler策略要求您在測試中執行http請求,這不是我在測試中想要的。我希望模擬 Guzzle 的 http 客戶端並傳回我可以在測試中指定的自訂 http 回應。我嘗試像這樣模擬 Guzzle 的 http 客戶端:

public function testStoreInternalEndpointSuccessful(): void
{
    // arrange, params & headers are not important in this problem
    $params = [];
    $headers = [];

    $mock = new MockHandler([
        new GuzzleResponse(200, [], $contactResponse),
    ]);

    $handlerStack = HandlerStack::create($mock);
    $client = new Client(['handler' => $handlerStack]);

    $mock = Mockery::mock(Client::class);
    $mock
        ->shouldReceive('create')
        ->andReturn($client);

    // act
    $response = $this->json('POST', '/v1/internal-endpoint', $params, $headers);

    // assert
    $response->assertResponseStatus(Response::HTTP_OK);
}

但是 InternalService 在測試中似乎沒有命中這個模擬。

我也考慮過並嘗試使用 Http Fake,但它不起作用,我認為 Guzzle 的 http 客戶端沒有擴展 Laravel 的 http 客戶端。

解決此問題並模擬第三方端點的最佳方法是什麼?

編輯

受到 StackOverflow 問題的啟發,我透過將具有模擬回應的 Guzzle 用戶端注入到我的服務中,成功解決了這個問題。與上述StackOverflow 問題的差別在於,我必須使用$this->app->singleton 而不是$this->app->bind 因為我的DI 配置不同:

AppServiceProvider.php

#
namespace AppProviders;

use AppServiceInternalService;
use GuzzleHttpClient;
use IlluminateSupportServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // my app uses ->singleton instead of ->bind
        $this->app->singleton(InternalService::class, function () {
            return new InternalService(new Client([
                'base_uri' => config('app.internal.base_url'),
            ]));
        });

    }
}


#
P粉842215006
P粉842215006

全部回覆(1)
P粉617597173

根據您的依賴注入,您希望使用返回模擬回應的自訂Guzzle http 用戶端來bindsingleton 化您的InternalService ,例如像這樣:

public function testStoreInternalEndpointSuccessful(): void
{

    // depending on your DI configuration,
    // this could be ->bind or ->singleton
    $this->app->singleton(InternalService::class, function($app) {
        $mockResponse = json_encode([
            'data' => [
                'id' => 0,
                'name' => 'Jane Doe',
                'type' => 'External',
                'description' => 'Etc. you know the drill',
            ]
        ]);

        $mock = new GuzzleHttp\Handler\MockHandler([
            new GuzzleHttp\Psr7\Response(200, [], $mockResponse),
        ]);

        $handlerStack = GuzzleHttp\HandlerStack::create($mock);
        $client = new GuzzleHttp\Client(['handler' => $handlerStack]);

        return new InternalService($client);
    });

    // arrange, params & headers are not important in this problem
    $params = [];
    $headers = [];

    // act
    $response = $this->json('POST', '/v1/internal-endpoint', $params, $headers);

    // assert
    $response->assertResponseStatus(Response::HTTP_OK);
}

另請參閱:使用 PHPUnit 在 Laravel 控制器內進行單元測試

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!