Heim > Fragen und Antworten > Hauptteil
In einem Laravel-Projekt (Laravel 8 auf PHP 8.0) habe ich einen Funktionstest, bei dem die internen Endpunkte getestet werden. Der Endpunkt verfügt über einen Controller, der eine Methode für den Dienst aufruft. Der Dienst versucht dann, den Endpunkt des Drittanbieters anzurufen. Es ist dieser Endpunkt eines Drittanbieters, den ich simulieren möchte. Die aktuelle Situation ist wie folgt:
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; } }
Ich habe mir die Dokumentation von Guzzle angesehen, aber es scheint, dass MockHandler
Die Strategie erfordert, dass Sie in Ihren Tests http-Anfragen ausführen, was in meinen Tests nicht das ist, was ich möchte. Ich möchte den http-Client von Guzzle verspotten und eine benutzerdefinierte http-Antwort zurückgeben, die ich in meinem Test angeben kann. Ich versuche, den http-Client von Guzzle wie folgt zu emulieren:
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); }
Aber InternalService
diese Simulation scheint beim Testen nicht zu überzeugen.
Ich habe auch über die Verwendung von Http Fake nachgedacht und es versucht, aber es hat nicht funktioniert. Ich denke, der http-Client von Guzzle erweitert den http-Client von Laravel nicht.
Was ist der beste Weg, dieses Problem zu lösen und den Endpunkt eines Drittanbieters zu verspotten?
Inspiriert durch diese StackOverflow-Frage habe ich dieses Problem erfolgreich gelöst, indem ich einen Guzzle-Client mit einer simulierten Antwort in meinen Dienst eingefügt habe. Der Unterschied zur obigen StackOverflow-Frage besteht darin, dass ich $this->app->singleton
而不是 $this->app->bind
verwenden musste, weil meine DI-Konfiguration anders war:
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粉6175971732023-11-10 09:09:10
根据您的依赖注入,您希望使用返回模拟响应的自定义 Guzzle http 客户端来bind
或 singleton
化您的 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 控制器内进行单元测试