Dans un projet Laravel (Laravel 8 sur PHP 8.0) j'ai un test fonctionnel où les points de terminaison internes sont testés. Le point de terminaison dispose d'un contrôleur qui appelle une méthode sur le service. Le service tente ensuite d'appeler le point de terminaison tiers. C'est ce point de terminaison tiers que je souhaite simuler. La situation actuelle est la suivante :
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; } }
J'ai regardé la documentation de Guzzle, mais il semble que MockHandler
La stratégie vous oblige à effectuer des requêtes http dans vos tests, ce qui n'est pas ce que je souhaite dans mes tests. Je souhaite me moquer du client http de Guzzle et renvoyer une réponse http personnalisée que je peux spécifier dans mon test. J'essaie d'émuler le client http de Guzzle comme ceci :
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); }
Mais InternalService
cette simulation ne semble pas fonctionner lors des tests.
J'ai aussi réfléchi et essayé d'utiliser Http Fake, mais cela n'a pas fonctionné, je pense que le client http de Guzzle n'étend pas le client http de Laravel.
Quelle est la meilleure façon de résoudre ce problème et de se moquer du point de terminaison tiers ?
Inspiré par cette question StackOverflow, j'ai réussi à résoudre ce problème en injectant à un client Guzzle une réponse simulée dans mon service. La différence avec la question StackOverflow ci-dessus est que j'ai dû utiliser $this->app->singleton
而不是 $this->app->bind
car ma configuration DI était différente :
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'), ])); }); } }
En fonction de votre injection de dépendances, vous souhaitez utiliser un client http Guzzle personnalisé qui renvoie une réponse simulée
bind
或singleton
化您的InternalService
, par exemple comme ceci :Voir aussi : Tests unitaires dans un contrôleur Laravel à l'aide de PHPUnit