Damon
Damon

Reputation: 4484

Laravel: How can I fake an http request to a 3rd party & mock json response

I'm struggling to figure out what is probably something that's very basic and I'll get laughed out of here but I'm hoping maybe it'll help someone else as well.

I am trying to mock/test an Http request in a feature test. I am still learning good/better/best techniques for testing so perhaps there is a much better way.

// MyFeatureTest.php

$user = factory(User::class)->create(['email' => '[email protected]']);

// Prevent actual request(s) from being made.
Http::fake();

$this->actingAs($user, 'api')
    ->getJson('api/v1/my/endpoint/123456')
    ->assertStatus(200);

In my controller my request looks like this:

public function myFunction() {
    try {
        $http = Http::withHeaders([
                'Accept' => 'application/json',
                'Access_Token' => 'my-token',
                'Content-Type' => 'application/json',
            ])
            ->get('https://www.example.com/third-party-url, [
                'foo' => 'bar,
        ]);

            
        return new MyResource($http->json());
    } catch (RequestException $exception) {
        Log::error("Exception error: " . print_r($exception, true));
    }
}

I would like to mock that I'm getting a 200 response, and ideally mock the expected json from the resource. I've successfully been able to do this test when the endpoint is local to my application (does not call to a 3rd party). This is what I've done in the past:

$http->assertStatus(200)
    ->assertJsonStructure([
        'type', 'id', 'attributes' => [
            'email', 'uuid', 'created_at', 'updated_at',
        ],
    ])
    ->assertJson(['type' => 'example',...]);

In the docs I can see this:

Http::fake([
    'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),
]);

How can I mock/fake the request to a 3rd party url and assert a good response? Thank you for any suggestions!

Upvotes: 3

Views: 18111

Answers (2)

Rwd
Rwd

Reputation: 35180

As per the docs (and your question) you can pass an array to Http::fake() to specify what response you would like for which requests i.e. the key is the request url and the value is the mocked response.

Your test would be something like:

$user = factory(User::class)->create(['email' => '[email protected]']);

Http::fake([
    'www.example.com/third-party-url' => Http::response([
        'type'       => 'example',
        'id'         => 'some id',
        'attributes' => [
            'email'      => 'some email',
            'uuid'       => 'some uuid',
            'created_at' => 'some created_at',
            'updated_at' => 'some updated_at',
        ],
    ], 200),
]);

$this->actingAs($user, 'api')
     ->getJson('api/v1/my/endpoint/123456')
     ->assertStatus(200)
     ->assertJsonStructure([
         'type',
         'id',
         'attributes' => [
             'email',
             'uuid',
             'created_at',
             'updated_at',
         ],
     ])
     ->assertJson(['type' => 'example', ...]);;

Upvotes: 11

Mayuresh
Mayuresh

Reputation: 81

You can use

use GuzzleHttp\Handler\MockHandler;

Your myFunction code will be something like

$mock = new MockHandler([
        new Response(200, [], File::get(base_path('your-mock-response-success.json'))),
    ]);

    $handler = HandlerStack::create($mock);

    $client = new Client(['handler' => $handler]);

    $mock = $this->mock(YourService::class);
    $mock->shouldReceive('create')
        ->andReturn($client);

Upvotes: 1

Related Questions