I have a Controller function, that expects the header 'X-Bla-Bla' from my JSON call. I catch the header with this:
$this->request->header('X-Bla-Bla')
Now I want to write a test for this but I can't send headers.
My test looks like this:
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
$url = Router::url(array('api' => true, 'controller' => 'test', 'action' => 'index'));
$options = array(
'return' => 'contents',
);
$result = $this->testAction($url, $options);
$this->assertNotEmpty($result);
How can I send the header?
If not, how can I still test my function?
If you set header this way in the test:
$_SERVER['HTTP_X_BLA_BLA'] = 'abc';
before calling testAction(), then your controller's action will be able to read 'abc' with expression:
$this->request->header('X-Bla-Bla')
Related
Is there a function to generate the corresponding URL given the request object?
For example, if the request params values are:
params => array(
'plugin' => 'plugin',
'controller' => 'foo',
'action' => 'bar',
'named' => array(),
'pass' => array()
)
data => array()
query => array(
'key' => 'val'
)
)
Then, generate the URL:
http://domain.com/plugin/foo/bar?key=val
Whether this fits your needs depends of course, but generally Router::reverse() is able to build a URL from a request object:
Router::reverse($cakeRequestObject, true)
It's much like Router::url(), but it will do all the dirty work for you like including the query values and removing unnecessary parameters.
Just Use Router::reverse() like that way :
$url = Router::reverse($this->params);
echo $url;
I am trying to write a phpunit test for a Laravel controller which expects post requests with a body in JSON format.
A simplified version of the controller:
class Account_Controller extends Base_Controller
{
public $restful = true;
public function post_login()
{
$credentials = Input::json();
return json_encode(array(
'email' => $credentials->email,
'session' => 'random_session_key'
));
}
}
Currently I have a test method which is correctly sending the data as urlencoded form data, but I cannot work out how to send the data as JSON.
My test method (I used the github gist here when writing the test)
class AccountControllerTest extends PHPUnit_Framework_TestCase {
public function testLogin()
{
$post_data = array(
'email' => 'user#example.com',
'password' => 'example_password'
);
Request::foundation()->server->set('REQUEST_METHOD', 'POST');
Request::foundation()->request->add($post_data);
$response = Controller::call('account#login', $post_data);
//check the $response
}
}
I am using angularjs on the frontend and by default, requests sent to the server are in JSON format. I would prefer not to change this to send a urlencoded form.
Does anyone know how I could write a test method which provides the controller with a JSON encoded body?
In Laravel 5, the call() method has changed:
$this->call(
'PUT',
$url,
[],
[],
[],
['CONTENT_TYPE' => 'application/json'],
json_encode($data_array)
);
I think that Symphony's request() method is being called:
http://symfony.com/doc/current/book/testing.html
This is how I go about doing this in Laravel4
// Now Up-vote something with id 53
$this->client->request('POST', '/api/1.0/something/53/rating', array('rating' => 1) );
// I hope we always get a 200 OK
$this->assertTrue($this->client->getResponse()->isOk());
// Get the response and decode it
$jsonResponse = $this->client->getResponse()->getContent();
$responseData = json_decode($jsonResponse);
$responseData will be a PHP object equal to the json response and will allow you to then test the response :)
Here's what worked for me.
$postData = array('foo' => 'bar');
$postRequest = $this->action('POST', 'MyController#myaction', array(), array(), array(), array(), json_encode($postData));
$this->assertTrue($this->client->getResponse()->isOk());
That seventh argument to $this->action is content. See docs at http://laravel.com/api/source-class-Illuminate.Foundation.Testing.TestCase.html#_action
There is a lot easier way of doing this. You can simply set Input::$json property to the object you want to send as post parameter. See Sample code below
$data = array(
'name' => 'sample name',
'email' => 'abc#yahoo.com',
);
Input::$json = (object)$data;
Request::setMethod('POST');
$response = Controller::call('client#create');
$this->assertNotNull($response);
$this->assertEquals(200, $response->status());
I hope this helps you with your test cases
Update : The original article is available here http://forums.laravel.io/viewtopic.php?id=2521
A simple solution would be to use CURL - which will then also allow you to capture the 'response' from the server.
class AccountControllerTest extends PHPUnit_Framework_TestCase
{
public function testLogin()
{
$url = "account/login";
$post_data = array(
'email' => 'user#example.com',
'password' => 'example_password'
);
$content = json_encode($post_data);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $content);
$json_response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
$response = json_decode($json_response, true);
// Do some $this->Assert() stuff here on the $status
}
}
CURL will actually simulate the raw HTTP post with JSON - so you know you are truly testing your functionality;
As of Laravel 5.1 there is a much easier way to test JSON controllers via PHPunit. Simply pass an array with the data and it'll get encoded automatically.
public function testBasicExample()
{
$this->post('/user', ['name' => 'Sally'])
->seeJson([
'created' => true,
]);
}
From the docs: http://laravel.com/docs/5.1/testing#testing-json-apis
Since at least Laravel 5.2 there is a json() method in Illuminate\Foundation\Testing\Concerns\MakesHttpRequests therefore you can do the following:
$data = [
"name" => "Foobar"
];
$response = $this->json('POST', '/endpoint', $data);
Also since Laravel 5.3 there are also convenient methods like putJson(), postJson(), etc. Therefore it can be even shortened further to:
$data = [
"name" => "Foobar"
];
$response = $this->postJson('/endpooint', $data);
And then you can do $response->assertJson(...) like:
$response->assertJson(fn (AssertableJson $json) => $json->hasAll(['id', 'name']));
I am trying to test a controller function that accepts a json payload.
As per the documentation of testAction() this can be done via setting $options['data'] to the appropriate string. Its not working for me.
See the documentation quoted here: http://api20.cakephp.org/class/controller-test-case (Please scroll down the the testAction() section).
Here is my test case.
public function testCreate(){
//Some code here
$this->testAction('/shippingbatches/create', array('data' => '[3,6]', 'method' => 'post'));
//Some more code here
}
Here is my controller function
public function create(){
debug($this->request); //This debug shows me an empty array in $this->request->data
ob_flush();
$order_ids = json_decode($this->request->data);
//Some more code here
}
The first line of the controller function is showing me an empty array in $this->request->data. If the 'data' passed from the testAction() is an actual array it comes in nice & fine. But not when it is set to a string (unlike it says in the documentation).
Here is the output of the debug.
object(Mock_CakeRequest_ef4431a5) {
params => array(
'plugin' => null,
'controller' => 'shippingbatches',
'action' => 'create',
'named' => array(),
'pass' => array(),
'return' => (int) 1,
'bare' => (int) 1,
'requested' => (int) 1
)
data => array()
query => array(
'case' => 'Controller\ShippingBatchesController'
)
url => 'shippingbatches/create'
base => ''
webroot => '/'
here => '/shippingbatches/create'
}
Please help.
Gurpreet
When passing data like that, you must receive it using CakeRequest::input().
public function create() {
$json = $this->request->input('json_decode', true);
debug($json);
}
I should note that I discovered this by reading Cake's test cases for ControllerTestCase::testAction. Reading test cases can give you insight into how Cake's internals work and give you hints on writing tests.
I have been searching all around the internet on how to mock cake requests. i want to stub out the data function to make $this->request->data('whatever') available in the controller. but something is going wrong with my test case
$Jobs = $this->generate('Tasks' , array(
'components' => array(
'RequestHandler' => array('isMobile','prefers','renderAs'))
));
// Mock CakeRequest
$request = $this->getMock('CakeRequest', array('_readInput'));
$Jobs->RequestHandler->request = $request;
$Jobs->RequestHandler->request->expects($this->any())
->method('data')->with('anything')->will($this->returnValue('test'));
$result = $this->testAction('/tasks/test/',
array('method' => 'get', 'return' => 'vars'));
whenever i call $this->request->data('anything') in the controller it returns null!
Please try to help me with this
From the PhpUnit documentation :
By default, all methods of the given class are replaced with a test double that just returns NULL unless a return value is configured using will($this->returnValue()), for instance.
When the second (optional) parameter is provided, only the methods whose names are in the array are replaced with a configurable test double. The behavior of the other methods is not changed.
So you need to either do this :
$Jobs = $this->generate('Tasks' , array(
'components' => array(
'RequestHandler' => array('isMobile','prefers','renderAs'))
));
// Mock CakeRequest
$request = $this->getMock('CakeRequest', array('_readInput'));
$Jobs->RequestHandler->request = $request;
$Jobs->RequestHandler->request->expects($this->any())
->method('_readInput')->with('anything')->will($this->returnValue('test'));
$result = $this->testAction('/tasks/test/',
array('method' => 'get', 'return' => 'vars'));
or this :
$Jobs = $this->generate('Tasks' , array(
'components' => array(
'RequestHandler' => array('isMobile','prefers','renderAs'))
));
// Mock CakeRequest
$request = $this->getMock('CakeRequest', array('data'));
$Jobs->RequestHandler->request = $request;
$Jobs->RequestHandler->request->expects($this->any())
->method('data')->with('anything')->will($this->returnValue('test'));
$result = $this->testAction('/tasks/test/',
array('method' => 'get', 'return' => 'vars'));
As I don't know cakePHP I can't tell you which is the right answer.
But according to this : http://api20.cakephp.org/view_source/controller-test-dispatcher
(line 232), you should try the former one.
I want to test a function with this header:
public function includeNumComments($posts){
Where $post is an array of data.
I wonder how can i test the method passing it an array of posts.
I have tried things like this, but it doesn't work:
$result = $this->testAction("/comments/includeNumComments/", array('data' => $posts));
$result = $this->testAction("/comments/includeNumComments/", array($posts));
Thanks
Here's the correct case, it worked for me. Hope it helps:
public function testAddUser() {
$data = array(
'User' => array(
'id' => 12,
'username' => 'testname1',
'password' => 'Pass#123!',
'email' => 'test#example.com'
)
);
$result = $this->testAction(
'/users/add',
array('data' => $data, 'method' => 'post')
);
debug($result);
}
That's not really using testAction then, because you can't pass an array via HTTP. There'd be no way to do this via a form or link on a website.
You can just test it as a normal function:
$result = $this->CommentsController->includeNumComments($posts);