How do I mock guzzle request - laravel

How do I make a mock of third party api call . That is happening from controller . I have this line of code in controller .
public function store(){
$response = $request->post('http://thirdpaty.app/rmis/api/ebp/requests', [
"headers" => [
'Content-Type' => 'application/json',
],
"json" => [
"data"=>1
]
]);
$data = json_decode($response->getBody()->getContents());
$token = $data->token;
// Saving that token to database
}
And from the test I am doing
$response = $this->post('/to-store-method');
How do I mock the api request . So that in testing I don't have to call the third api request .
Right now I am doing
if(app()->get('env') == 'testing'){
$token = 123;
}else{
//Api call here
}
Is there any better alternative of doing this test

You'll need some way of injecting a mock handler into the Guzzle Client your controller is using. Traditionally, you'd either leverage dependency injection by passing the Guzzle Client via the constructor or through some referenced service in that code that you can mock (using Mockery) behind the scenes.
After that, check out the Guzzle documentation for a peak on how to mock requests in the HTTP Client:
http://docs.guzzlephp.org/en/stable/testing.html
You'd use a MockHandler to do something resembling the following code by building a stack of fake requests and responses.
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'),
new Response(202, ['Content-Length' => 0]),
new RequestException('Error Communicating with Server', new Request('GET', 'test'))
]);
$handlerStack = HandlerStack::create($mock);
$client = new Client(['handler' => $handlerStack]);
// The first request is intercepted with the first response.
$response = $client->request('GET', '/');

Actually, it is a bad practice to mock the network libraries. What I would recommend is to wrap the network request by the httpService and mock the httpService instead to return the required response.
public function store(){
$response = httpService.postData();
$data = json_decode($response->getBody()->getContents());
$token = $data->token;
// Saving that token to database
}
So, the you would get the response as return from the httpService.postData function and you can mock the postData instead of the network library.

Related

problem with multiple HTTP requests in Laravel with Guzzle

I'm using Guzzle version 6.3.3. I want to make multiple HTTP requests from an external API. The code shown below worker perfect for me. This is just one single request.
public function getAllTeams()
{
$client = new Client();
$uri = 'https://api.football-data.org/v2/competitions/2003/teams';
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$res = $client->get($uri, $header);
$data = json_decode($res->getBody()->getContents(), true);
return $data['teams'];
}
But now I want to make multiple requests at once. In the documentation of Guzzle I found out how to do it, but it still didn't work properly. This is the code I try to use.
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$client = new Client(['debug' => true]);
$res = $client->send(array(
$client->get('https://api.football-data.org/v2/teams/666', $header),
$client->get('https://api.football-data.org/v2/teams/1920', $header),
$client->get('https://api.football-data.org/v2/teams/6806', $header)
));
$data = json_decode($res->getBody()->getContents(), true);
return $data;
I get the error:
Argument 1 passed to GuzzleHttp\Client::send() must implement interface Psr\Http\Message\RequestInterface, array given called in TeamsController.
If I remove the $header after each URI then I get this error:
resulted in a '403 Forbidden' response: {"message": "The resource you are looking for is restricted. Please pass a valid API token and check your subscription fo (truncated...)
I tried several ways to set X-Auth-Token with my API key. But I still get errors and I don't know many other ways with Guzzle to set them.
I hope someone can help me out :)
Guzzle 6 uses a different approach to Guzzle 3, so you should use something like:
use function GuzzleHttp\Promise\all;
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$client = new Client(['debug' => true]);
$responses = all([
$client->getAsync('https://api.football-data.org/v2/teams/666', $header),
$client->getAsync('https://api.football-data.org/v2/teams/1920', $header),
$client->getAsync('https://api.football-data.org/v2/teams/6806', $header)
])->wait();
$data = [];
foreach ($responses as $i => $res) {
$data[$i] = json_decode($res->getBody()->getContents(), true);
}
return $data;
Take a look at different questions on the same topic (#1, #2) to see more usage examples.

Laravel 5.6 Mock Guzzle Response

I am trying to mock a guzzle response from a specific api.
My controller code looks like this (amended for brevity):
class SomeClass
{
private $guzzle;
public function __construct(\GuzzleHttp\Client $guzzle) {
$this->guzzle = new $guzzle();
}
public function makeRequest(){
$client = $this->guzzle;
$url = 'http//somerurl';
$options = [];
$response = $client->request('POST', $url, $options);
return $response;
}
}
And the test looks something like this (again edited)...
public function someTest(){
$mock = $this->createMock(\GuzzleHttp\Client::class);
$mock->method('request')->willReturn([
'response' => 'somedata'
]);
$someClass = new $SomeClass($mock);
$response = $someClass->makeRequest();
$body = $response->getBody();
...
}
At this point the test returns "Call to a member function getBody on null";
How can the getBody response of a guzzle call be tested?
Thank you in advance...
One approach to testing with Guzzle is to configure a MockHandler
http://docs.guzzlephp.org/en/stable/testing.html
So instead of mocking the guzzle client, you create one like so:
public function someTest() {
$mock = new MockHandler([
new Response(200, [], 'The body!'),
// Add more responses for each response you need
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$someClass = new SomeClass($client);
$response = $someClass->makeRequest();
$body = $response->getBody();
$this->assertSame('The body!', $body);
}
The MockHandler requires you to 'queue' responses, meaning you need to know in what order external API calls will be made. I've taken this a step further and wrapped the MockHandler in another handler capable of stuffing a dummy-response into it at the last moment, if one isn't already waiting in the wings. See https://gist.github.com/kmuenkel/d4d473beb7b2297ac2d8cd480089a738
Just use that trait in your test, and call $this->mockGuzzleResponses(); from the test class's setUp() method. At that point, all requests intended to pass through Guzzle will be available for assertions by way of the $guzzleRequestLog property, and all responses can be mocked by calling $this->guzzleHandler->append(RequestInterface); at the beginning of your test.
Just make sure that all implementations of Guzzle in your code are instantiated by way of the app(Client::class) helper and not new Client. Otherwise the binding override won't take effect. That may have been your issue earlier.
Take a look at my composer package https://packagist.org/packages/doppiogancio/mocked-client.
In my opinion, it's a really simple way to mock a Guzzle Client, binding request URLs with responses.
$builder = new HandlerStackBuilder();
// Add a route with a response via callback
$builder->addRoute(
'GET', '/country/IT', static function (ServerRequestInterface $request): Response {
return new Response(200, [], '{"id":"+39","code":"IT","name":"Italy"}');
}
);
// Add a route with a response in a text file
$builder->addRouteWithFile('GET', '/country/IT/json', __DIR__ . '/fixtures/country.json');
// Add a route with a response in a string
$builder->addRouteWithString('GET', '/country/IT', '{"id":"+39","code":"IT","name":"Italy"}');
// Add a route mocking directly the response
$builder->addRouteWithResponse('GET', '/admin/dashboard', new Response(401));
$client = new Client(['handler' => $builder->build()]);
Once you did you will have a fully functional client to use normally
$response = $client->request('GET', '/country/DE/json');
$body = (string) $response->getBody();
$country = json_decode($body, true);
print_r($country);
// will return
Array
(
[id] => +49
[code] => DE
[name] => Germany
)

Laravel: Is it possible to directly test the json response output of a function without actually going through the URI?

From the docs, I can test some json returned from my app using the following:
$response = $this->json('POST', '/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertJson([
'created' => true,
]);
However, is it possible to bypass actually calling up the URI with $this->json(*method*, *uri*, *data*); and instead test the direct output of a controller function which returns json? For example, I want to do something like this:
// My controller:
function getPageData(){
$data = array('array', 'of', 'data');
return response()->json($data);
}
// My Test Class:
$controller = new Primary();
$response = $controller->getPageData();
$response->assertJson([
'array', 'of', 'data'
]);
Is this possible?
You could do this for some basic methods, but it might cause side effects:
app(SomeController::class)->someControllerMethod();
Basically, the app() will resolve the dependencies from the constructor, but it won't resolve the method dependencies. So if you typehint something like method(Request $request), it will throw an error.
I'm pretty sure dealing with request() will cause unintentional effects since there's no real request going on.
Edit:
You could then create a TestResponse object, to get all the asserts as well:
$res = app(SomeController::class)->someControllerMethod();
$testRes = new Illuminate\Foundation\Testing\TestResponse($res);
$testRes->assertJson(...); // Will be available

How to write on route to receive request from external laravel project

$client = new Client();
$response =
$client->GET('http://XXX.XX.XX.XX/eDeskApi/module/SOMEFUNCTION',
[
'json' => ['foo' => 'bar']
]);
This is how i'm sending a request to external LARAVEL api.
I just want to know what do i need to write in laravel api router to get something return when i send this request.
Currently i have the following code on laravel api router
Route::GET('module/url', 'Nameof Controller#NameOfMethhod');
And in controller looks like:
public function GetApplicationModuleList()
{
echo 'something';
//i want to print the parameter value which i just sent by above mention request.
}
You need to change your request from GET to POST if you want to send some request body there.
Also your function should take an argument of:
Illuminate\Http\Request type.
After that you can access you request body by doing:
$request -> all();

Symfony2 functional test with Session and Post

I am trying to write some Symfony2 functional tests, using PHPUnit, that simulate a logged-in user requesting some resource via an AJAX call.
The test first simulates a user logging-in using the standard FOSUserBundle log-in form;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MyWebTestCase extends WebTestCase {
public function login($username, $password = 'password')
{
$client = static::createClient();
$client->followRedirects(true);
$crawler = $client->request('GET', '/login');
$form = $crawler->selectButton('Login')->form(array(
'_username' => $username,
'_password' => $password,
));
$crawler = $client->submit($form);
return $client;
}
}
Then, the test gets an entity from the database and sets it in a session variable, then sends a POST request to retrieve the requested resource, using some JSON which includes extra information about what is being requested (in the actual application, some JavaScript composes the JSON in response to the user's actions which is then submitted via AJAX);
$this->client = $this->login('user#domain.com', 'password');
// we want element number 6 from the fixtures
$chart = $this->em->getRepository('MyBundle:Element')->find(6);
$guid = $chart->getGuid();
$this->client->getContainer()->get('session')->set($guid, $chart);
$link = sprintf('/viewer/data/%d/%s', 1, $guid);
$crawler = $this->client->request('POST', $link, array(), array(),
array('CONTENT_TYPE' => 'application/json'),
'{"filters":[]}');
When the tests reach this point, an error is generated;
ini_set(): A session is active. You cannot change the session module's ini settings at this time
I'm using Symfony 2.6.5 and PHPUnit 4.5.0
Do you use mock sessions for your test? If not try this.
Config_test.yml
framework:
test: ~
session:
storage_id: session.storage.mock_file
The native session storage is meant to handle a single request per process which may lead to your bug. if its your case.

Resources