Laravel testing, get JSON content - laravel

In Laravel's unit test, I can test a JSON API like that:
$this->post('/user', ['name' => 'Sally'])
->seeJson([
'created' => true,
]);
But what if I want to use the response. How can I get the JSON response (as an array) using $this->post()?

Proper way to get the content is:
$content = $this->get('/v1/users/1')->decodeResponseJson();

Currently, in 5.3 this is working...
$content = $this->get('/v1/users/1')->response->getContent();
However, it does break the chain since response returns the response and not the test runner. So, you should make your chainable assertions before fetching the response, like so...
$content = $this->get('/v1/users/1')->seeStatusCode(200)->response->getContent();

As at Laravel 8, this worked for me.
I was returning an automatically generated field (balance) after the POST request has created the entity.
The response was in the structure {"attributes":{"balance":12345}}
$response = $this->postJson('api/v1/authors', [
'firstName' => 'John',
'lastName' => 'Doe',
])->assertStatus(201);
$balance = $response->decodeResponseJson()['attributes']['balance'];
decodeResponseJson will pick the response and transform it to an array for manipulation.
Using getContent() returns json and you will have to use json_decode on the returned data to turn it into an array.

I like to use the json method when working with json, instead of ->get()
$data = $this->json('GET', $url)->seeStatusCode(200)->decodeResponseJson();

I hit a similar problem and could not get $this->getResponse()->getContent() working with the built in $this->get() method. I tried several variations with no success.
Instead I had to change the call to return the full http response and get the content out of that.
// Original (not working)
$content = $this->get('/v1/users/1')->getContent();
// New (working)
$content = $this->call('GET', '/v1/users/1')->getContent();

Simple way:
$this->getJson('api/threads')->content()

Just want to share, I have used the same in $this->json() like:
$response = $this->json('POST', '/order', $data)->response->getContent();
But I added one more line to use json response and decode otherwise decodeResponseJson() was not working for me.
$json = json_decode($response);

Found a better way:
$response = $this->json('POST', '/order', $data);
$responseData = $response->getOriginalContent(); // saves the response as an array
$responseData['value'] // you can now access the array values
This method returns the response json as an array.

You can just call:
$data = $response->json();

$response = $this->json('POST', '/products', $data);
$data = $response->getData();

Related

Laravel validation couldn't store value after validate and give error 500

I have a form that using ajax for update data client. In that form there is an input file. Everything is going fine except for updating the file. File is sent, it changed on storage too, but it gives error on validation and didn't change data on database.
Here is the code on the controller :
public function update(Request $request, Client $client)
{
$validatedData = Validator::make($request->all(), [
'name' => 'required|max:255',
'logo'=> 'image|file|max:100',
'level' => 'required|max:1'
]);
$validatedData['user_id'] = auth()->user()->id;
if ($validatedData->fails()){
return response()->json($validatedData->errors());
} else {
if($request->file('logo')){
if($request->oldLogo){
Storage::delete($request->oldLogo);
}
$validatedData['logo'] = $request->file('logo')->store('logo-clients');
}
$validateFix = $validatedData->validate();
Client::where('id', $client->id)->update($validateFix);
return response()->json([
'success' => 'Success!'
]);
}
}
It gives error on line :
$validatedData['logo'] = $request->file('logo')->store('logo-clients');
With message :
"Cannot use object of type Illuminate\Validation\Validator as array"
I use the same code that works on another case, the difference is the other not using ajax or I didn't use Validator::make on file input. I guess it's just wrong syntax but I don't really know where and what it is.
To retrieve the validated input of a Validator, use the validated() function like so:
$validated = $validator->validated();
Docs:
https://laravel.com/docs/9.x/validation#manually-creating-validators
https://laravel.com/api/9.x/Illuminate/Contracts/Validation/Validator.html
$validatedData is an object of type Illuminate\Validation\Validator.
I would say the error is earlier there as well as this line should give an error also:
$validatedData['user_id'] = auth()->user()->id;
As ericmp said, you first need to retrieve the validateddata to an array and then work with it.

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: 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 change value of a request parameter in laravel

I need to change value of my request parameter like this:
$request->name = "My Value!";
I use this code but does not work:
$request->offsetSet('img', $img);
Try to:
$requestData = $request->all();
$requestData['img'] = $img;
Another way to do it:
$request->merge(['img' => $img]);
Thanks to #JoelHinz for this.
If you want to add or overwrite nested data:
$data['some']['thing'] = 'value';
$request->merge($data);
If you do not inject Request $request object, you can use the global request() helper or \Request:: facade instead of $request
Use merge():
$request->merge([
'user_id' => $modified_user_id_here,
]);
Simple! No need to transfer the entire $request->all() to another variable.
Read more about Laravel's merge() here:
https://laravel.com/docs/collections#method-merge
If you need to customize the request
$data = $request->all();
you can pass the name of the field and the value
$data['product_ref_code'] = 1650;
and finally pass the new request
$last = Product::create($data);
If you need to update a property in the request, I recommend you to use the replace method from Request class used by Laravel
$request->replace(['property to update' => $newValue]);
Use add
$request->request->add(['img' => $img]);
If you use custom requests for validation, for replace data for validation, or to set default data (for checkboxes or other) use override method prepareForValidation().
namespace App\Http\Requests\Admin\Category;
class CategoryRequest extends AbstractRequest
{
protected function prepareForValidation()
{
if ( ! $this->get('url')) {
$this->merge([
'url' => $this->get('name'),
]);
}
$this->merge([
'url' => \Str::slug($this->get('url')),
'active' => (int)$this->get('active'),
]);
}
}
I hope this information will be useful to somebody.
It work for me
$request = new Request();
$request->headers->set('content-type', 'application/json');
$request->initialize(['yourParam' => 2]);
check output
$queryParams = $request->query();
dd($queryParams['yourParam']); // 2
Great answers here but I needed to replace a value in a JSON request. After a little digging into the code, I came up with the following code. Let me know if I'm doing something dumb.
$json = $request->json()->all();
$json['field'] = 'new value';
$request->json()->replace($json);
Try that :
$request["name"] = "My New Value";
$request["img"] = $img;
It's worked in Laravel 8.
Also, make sure to update the model class.
Item
{
fillable=[
'img',
... // other attributes
];
}
in case of updating an item of object you can write the lines bellow
$Obj = $request->data;
$Obj['item'] = value;

AJAX Request getContent Laravel 5.1

$content = $request->getContent();
return \Response::json([
'success' => $content
]);
I don't know how to format it to json in laravel 5.1. I want to use the data but if I use $request->input('name') it is null, it's actually in the controller. If you have another way to get the data it would be much appreciated.
you would have to parse the response in the client-side:
JSON.parse(ajaxResponse)
right now you're only getting a string back from the server

Resources