Bagaimana untuk mensimulasikan permintaan kepada API pihak ketiga menggunakan klien GuzzleHttp dalam ujian fungsi Laravel?
P粉842215006
P粉842215006 2023-11-09 11:42:58
0
1
713

Dalam projek Laravel (Laravel 8 pada PHP 8.0) saya mempunyai ujian berfungsi di mana titik akhir dalaman diuji. Titik akhir mempunyai pengawal yang memanggil kaedah pada perkhidmatan. Perkhidmatan kemudiannya cuba menghubungi titik akhir pihak ketiga. Titik akhir pihak ketiga inilah yang ingin saya simulasikan. Keadaan semasa adalah seperti berikut:

Ujian fungsi titik akhir dalaman

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

Pengawal Titik Akhir Dalaman

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.
    }
}

Servis dalaman

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

Saya telah melihat dokumentasi Guzzle, tetapi nampaknya MockHandlerStrategi memerlukan anda melaksanakan permintaan http dalam ujian anda, yang bukan yang saya inginkan dalam ujian saya. Saya ingin mengejek klien http Guzzle dan mengembalikan respons http tersuai yang boleh saya tentukan dalam ujian saya. Saya cuba mencontohi pelanggan http Guzzle seperti ini:

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

Tetapi InternalService simulasi ini nampaknya tidak berkesan dalam ujian.

Saya juga memikirkan dan mencuba menggunakan Http Fake, tetapi tidak berjaya, saya rasa klien http Guzzle tidak memanjangkan klien http Laravel.

Apakah cara terbaik untuk menyelesaikan masalah ini dan mengejek titik akhir pihak ke-3?

Sunting

Diilhamkan oleh soalan StackOverflow ini, saya berjaya menyelesaikan masalah ini dengan menyuntik pelanggan Guzzle dengan tindak balas yang diejek ke dalam perkhidmatan saya. Perbezaan daripada soalan StackOverflow di atas ialah saya terpaksa menggunakan $this->app->singleton 而不是 $this->app->bind kerana konfigurasi DI saya berbeza:

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

membalas semua(1)
P粉617597173

Bergantung pada suntikan kebergantungan anda, anda ingin menggunakan klien http Guzzle tersuai yang mengembalikan respons yang diejek bindsingleton 化您的 InternalService, contohnya seperti ini:

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

Lihat juga: Ujian unit di dalam pengawal Laravel menggunakan PHPUnit

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan