testing a POST using phpunit in laravel 4 - php

I have a route that does a POST to create data and I'm trying to test if everything should be working the way it should be.
I have a json string that will have the values that i want to test but so far the test is always failing when I run the test using phpunit:
Also,I know the json string is just a string but I'm also unsure of how to use the json string to test for input.
my route:
Route::post('/flyer', 'flyersController#store');
public function testFlyersCreation()
{
$this->call('POST', 'flyers');
//Create test json string
$json = '{ "name": "Test1", "email": "test#gmail.com", "contact": "11113333" }';
var_dump(json_decode($json));
}
When i run phpunit, my error points to the call POST that says "undefined index: name"

I'm not sure if i understand the question correctly, given the code example that actually does nothing, but if you're asking how to test a post route which requires json data in the request, take a look at the call() method:
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Foundation/Testing/ApplicationTrait.php
raw post data should be in the $content variable.
I don't have Laravel 4 installed to test it, but it works for me in Laravel 5, where the function has just slightly different order of params:
public function testCreateUser()
{
$json = '
{
"email" : "horst.fuchs#example.com",
"first_name" : "Horst",
"last_name" : "Fuchs"
}';
$response = $this->call('POST', 'user/create', array(), array(), array(), array(), $json);
$this->assertResponseOk();
}

If you look at the source code of TestCase you can see that the method is actually calling
call_user_func_array(array($this->client, 'request'), func_get_args());
So this means you can do something like this
$this->client->request('POST', 'flyers', $json );
and then you check the response with
$this->assertEquals($json, $this->client->getResponse());
The error you are getting is probably thrown by the controller because it doesnt receive any data

Related

Github Laravel workflow getting invalid JSON from routes

For the automated testing of a laravel API I am using the 'laravel' action on github actions, the one made by github actions.
The tests keep failing telling me invalid JSON returned from route, expected response code 200 but got 500, cannot read property status on null and cannot find in json
I'm using laravel sanctum. Could it be a csrf-token problem?
My action yml: https://gist.github.com/I2EJeffrey/77df8faac1b0f86623e2e4449f98d858
My response function:
* success response method.
*
* #return \Illuminate\Http\Response
*/
public function sendResponse($result, $message, $code = 200)
{
$response = [
'success' => true,
'data' => $result, // result is most often one or 2 arrays
'message' => $message,
];
return response()->json($response, 200);
}
Example test:
public function testSuccessfullyCreateAccommodationType()
{
$this->login(); // Login function that lots of tests need.
$response = $this->postJson('/api/v5/accommodations/1/types', ['accommodation_name'=>$this->createName()]);
$response
->assertJsonFragment(['success' => true])
->assertJsonStructure(['success', 'data' =>
[], 'message']); // The array is filled with keys
}
EDIT: 2 errors that I got by using withoutExceptionHandling: https://gist.github.com/I2EJeffrey/da23bfbdf5fba155456bd799a34f6276
EDIT 2: I also get the following warning: TTY mode requires /dev/tty to be read/writable.
EDIT 3: The client model and the client seeder. Whenever I run the tests a mysql docker container starts that gets a db migrated and seeded into it:
https://gist.github.com/I2EJeffrey/39c779df217c9a75a7569f6fa3957d77
https://gist.github.com/I2EJeffrey/41cbbdba8025d38547059d3a6f4d4392
My problem was that the DB didn't get seeded due to not having added $this->call(ClientSeeder::class); to the DatabaseSeeder. Which caused the routes to return null and thus wrong json.

Can I find unepexcted issues by creating dynamic properties on an object (via $this) on a callback function?

I have a question regarding the use of $this inside a Laravel routing.
Recently I'm trying to find a way to pass PHP variables between Route::group() and Route::get/put/post/anything-that-fits-inside-the-group(). So I tried to Google my way out and found most of the results always related to passing variable between routes and view (which is not the case). This result were closest to -- if not exactly -- what I'm trying to achieve, stating that variable passing can't be done in such way.
During my curiosity over this topic, I fiddled around with the routes, trying to pass the PHP variables between routes in a few ways until I found a solution to this case: by using $this
Here's a bit of my code just to give you some context:
...
Route::prefix("api")->middleware("verify_api_call")->group( function(){
[$this->success, $this->message, $this->data] = [false, "", null];
$this->ctx = [
"success" => &$this->success,
"message" => &$this->message,
"data" => &$this->data
];
Route::get("test", function(){
$this->success = true;
$this->message = "ok";
$this->data = [
"somekey" => "somevalue"
];
return response()->json(["Contextual" => $this->ctx, "All of this" => $this]);
});
...
Calling mysite.com/api/test gives the expected result as below:
{
"Contextual": {
"success": true,
"message": "ok",
"data": {
"somekey": "somevalue"
}
},
"All of this": {
"success": true,
"message": "ok",
"data": {
"somekey": "somevalue"
},
"ctx": {
"success": true,
"message": "ok",
"data": {
"somekey": "somevalue"
}
}
}
}
Now the question might be no longer about how it works, but rather: will there be any unexpected problems that I'm unaware of if I keep using $this this way?
TL;DR; Yes, you could cause undefined behaviour in future.
You're basically storing your custom variables on an instance of the class:
Illuminate\Routing\RouteFileRegistrar
And currently, said Class has only $router field, and each Group creates a new instance of said Class for itself.
But in future Laravel versions, said Class may have additional fields, which you may override by accident, or it could entirely change to another Class.
So, for now you are good to go, but consider using function () use($ctx) {} syntax instead, like:
Route::prefix("api")->middleware("verify_api_call")->group( function(){
[$success, $message, $data] = [false, "", null];
$ctx = [
"success" => & $success,
"message" => & $message,
"data" => & $data
];
Route::get("test", function() use($ctx) {
$ctx->success = true;
$ctx->message = "ok";
$ctx->data = [
"somekey" => "somevalue"
];
return response()->json(["Contextual" => $ctx]);
});
});
You are creating public dynamic properties on an object of class you don't control.
Two immediate problems:
The API of the class could change in the future, and your chosen properties may clash with whatever the class creator chooses.
Dynamic properties will be deprecated by PHP 8.2, and removed by PHP 9.0. So unless the class maintainer adds code to allow for developers to add random dynamic properties, your code will fail by then.
It's a bad idea and poor design.

Get () method to get specific data return in a post callback

I'm working on a project and I came across a problem, explain:
I'm doing a POST to a webserver using the Guzzle http, follows the :
public function sendPost($direction, array $data, array
$options = ['http_errors'=>false])
{
$url = $this->interpolate( self::BASE_PATH."/{direction}",
[
'direction' => $direction
]);
$options = array_merge($options,['body'=>json_encode($data)]);
return $this->client->post($url, $options);
}
The method is working correctly and I am returning the following:
{
"id": "jdhj9h830027hd73hs9q9js9",
"direction": "left",
"status": "Success",
"code": {
"id":"1",
"desc": "ok",
"error": false,
"msg":null
}
}
What I can not do is the following:
A method that returns only the given "jdhj9h830027hd73hs9q9js9", that is, the "id" parameter.
can anybody help me?
PS. By using the "sendPost ()" method I can access the data I need separately, however I can not do this through another method, such as a "getId ()".
Thank you in advance for your help.
Just try:
return ($this->client->post($url, $options))->id;

phpunit gives same response for multiple calls with different parameters

i recently moved from laravel 5.2 to laravel 5.4. In 5.2 all testcases were working fine. After upgrading to new version i am facing issues with testcases. Below is my sample code that was working fine in 5.2.
$params = [
'id' => 'asddsdsd'
];
$response1 = $this->call('GET', '/user/getInfo', $params);
$this->assertArrayHasKey('status', $response1);
$this->assertTrue($response1['status'] == 400);
$params2 = [
'id' => '23'
];
$response2 = $this->call('GET', '/user/getInfo', $params2);
$this->assertArrayHasKey('status', $response2);
$this->assertTrue($response2['status'] == 200);
in laravel 5.2 both used to have seperate response, in first case i used to get Failure response and second case, i was getting Success response.
After upgrading to 5.4, i am gettign same response for two testcases. Basically, the first call response is copied to second response because of calling same URl. This is happening for all the testcases.
Note: I added second case in different method, but still facing the same issue
Can anyone suggest me how to fix this??
Looks like the call() method returns a Response object in 5.2, but a TestResponse object in 5.4. To make your code comparable in 5.4, you could try using the baseResponse property of your TestResponse object:
$this->assertArrayHasKey('status', $response1->baseResponse);
You might also need to separate your 2 requests into separate modular test cases and then link them using #depends annotation.
public function testRequest1()
{
$params = [
'id' => 'asddsdsd'
];
$response1 = $this->call('GET', '/user/getInfo', $params);
//$this->assertWhatever()
return $dataIfYouWantIt
}
/**
* #depends testRequest1
*/
public function testRequest2($dataIfYouWantIt)
{
$params2 = [
'id' => '23'
];
$response2 = $this->call('GET', '/user/getInfo', $params2);
//$this->assertWhatever()
}

How to get response from action dispatched in phpunit

I am dispatching some POST data to an action of a controller. That action echoes some json-encoded string. I want to verify that the json-encoded string of that action is as I want it. I want to know how I can get that string?
My test looks like this:
$this->request->setMethod('POST')
->setPost(['test' => 'databaseschema_Database']);
$params = ['action' => 'analysis', 'controller' => 'Index', 'module' => 'default'];
$urlParams = $this->urlizeOptions($params);
$url = $this->url($urlParams);
$result = $this->dispatch($url);
$this->assertJsonStringEqualsJsonString(
$result, json_encode(["status" => "Success"])
);
My test is failing and I am getting following message:
1) IndexControllerTest::testAnalysisAction
Expected value JSON decode error - Unknown error
stdClass Object (...) does not match expected type "NULL".
Can any one guide me on how to do this?
If you want to do unit testing, what you really want to do is extract the json encoding into it's own class (or a method inside a utils class or something) and then test those method instead of your whole controller.
The problem with your approach is that when running phpunit, there is not $_POST array. The code above does not show what is happening, but I guess there is different behaviour when run through apache and cli which causes your test to fail.
I would create a TransformerClass and test this in isolation:
class JsonTransformer
{
public function transformPostData(array $postArray)
{
// transformation happening
}
}
class JsonTransformerTest extends \PHPUnit_Framework_TestCase
{
public function testTransformPostData()
{
$transformer = new JsonTransformer();
$data = array('action' => 'analysis', 'controller' => 'Index', 'module' => 'default');
$result = $transformer->transformPostData(data);
$this->assertJsonStringEqualsJsonString($result, json_encode(array("status" => "Success")));
}
}
If you need to test your whole request/response, you would use some kind of HTTPClient, request the url, send the post data and see if the response is what you'd expect.
Everything in between (like faking the post data) leaves you with more problems and more code to maintain than it does you good.

Categories