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:
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; } }
Saya telah melihat dokumentasi Guzzle, tetapi nampaknya MockHandler
Strategi 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?
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:
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'), ])); }); } }
Bergantung pada suntikan kebergantungan anda, anda ingin menggunakan klien http Guzzle tersuai yang mengembalikan respons yang diejek
bind
或singleton
化您的InternalService
, contohnya seperti ini:Lihat juga: Ujian unit di dalam pengawal Laravel menggunakan PHPUnit