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']));
Related
I have written a drupal module with custom form api which will send email to my inbox on each submit. I have written a condition under drupal_mail which returns true but shows an error message "Unable to send e-mail. Contact the site administrator if the problem persists."
Below my code:
function my_module_name_mail($key, &$message, $params)
{
$headers = array(
'MIME-Version' => '1.0',
'Content-Type' => 'text/html; charset=UTF-8;',
'Content-Transfer-Encoding' => '8Bit',
'X-Mailer' => 'Drupal'
);
foreach ($headers as $key => $value) {
$message['headers'][$key] = $value;
}
$message['subject'] = $params['subject'];
$message['body'] = $params['body'];
}
function my_module_name_form_submit($form, &$form_state)
{
$from = $form_state['values']['email'];
$body= 'Name: '.$name.'<br />Email: '.$email;
$to = "my_mail_id#example.com";
$params = array(
'body' => $body,
'subject' => 'Website Information Request',
);
drupal_mail('my_module_name', 'some_mail_key', $to, language_default(), $params, $from, TRUE);
}
Check your Drupal logs for more clues. Enable devel / devel-admin modules. Once you do, drupal_mail will pipe emails to your temp directory, which will help you narrow down if the problem is with your email server or the config.
I can't send an attachment with the email. I don't get an error and I do send the message so the email works but no attachment.
Is my filepath not correct as the file exists in this file? Is it because I am using windows with file paths?
This is just a test email below to see if this function actually works for an attachment but it isn't working for me. I checked other answers and this seems to be the way to construct this.
http://book.cakephp.org/2.0/en/core-utility-libraries/email.html
public function sendEmailattach($to,$message,$subject,$attach) {
$Email = new CakeEmail();
$Email->config('gmail3');
$Email->filePaths = array('D:\crm5\app\Attachments');
$Email->attachments =array('Ch9-anna tax.docx');
$to='jXXXXX#gmail.com';//testing real email account
// $Email->from( array('admin#a.com.au' => 'A'));
$Email->from( array('jxxxxx#gmail.com' => 'test'));
$Email->to($to);
$Email->subject($subject);
$Email->send();
// $Email->send($message);
}//public
UPDATE
Tried all 3 methods and no error and no attachment?
// $Email->attachments('D:\AA-website design\crm5\app\Attachments\Ch9-anna tax.docx') ;
$Email->attachments(array('Ch9-anna tax.docx' => array(
'file' => 'D:\AA-website design\crm5\app\Attachments\Ch9-anna tax.docx',
) ));
// $Email->attachments(array('D:\AA-website design\crm5\app\Attachments\Ch9-anna tax.docx'));
$email = new CakeEmail('default');
$attachment = [
'file.pdf' => [
'file' => '/my/absolute/path/on/server/file.pdf',
'mimetype' => 'application/pdf',
'contentId' => uniqid()
]
];
$variables = ['emailHeader' => 'Hello'];
$email->attachments($attachment);
$email->from(['info#example.com' => 'Example'])
->to('recipient#example.com')
->subject('Subject')
->template('template')
->emailFormat('both')
->viewVars($variables)
->send();
Just read the manual instead just looking at the links, honestly I have doubt's you read it at all:
http://book.cakephp.org/2.0/en/core-utility-libraries/email.html#sending-attachments
It clearly shows that attachments is a method and not a property. It even has examples of what the method accepts.
I'm working on an edit method. After saving data, an email is sent out with the changes made during the edit. Everything works except for one infuriating but crucial bug. Here it is distilled down very simply:
$data = $this->SupportTicket->readForView($st_id);
$this->SupportTicket->id = $st_id;
if ($this->SupportTicket->save($this->request->data)) {
//call custom model method to pull view data
$data = $this->SupportTicket->readForView($st_id);
//do something with this data
}
The issue is that $data comes out with the pre-save data. So what I then try to do with the new data doesn't work.
I can't just use $this->request->data because it doesn't have the full data that I want in it.
The save does however work. If I refresh the view method for the same record, it shows as updated. So it's saving, but when I do the find after saving it is giving me old data.
Any ideas?
Update: it doesn't happen with findById($st_id) so it must be something to do with my custom method. Code:
public function readForView($id)
{
$data = $this->find('first', array(
'conditions' => array(
'SupportTicket.id' => $id
),
'contain' => array(
'User',
'Owner'
)
));
if (empty($data)) {
throw new notFoundException('Ticket not found');
}
$data['SupportTicket']['type_long'] = $this->getLongType($data['SupportTicket']['type']);
$data['SupportTicket']['status_long'] = $this->getLongStatus($data['SupportTicket']['status']);
$data['SupportTicket']['name'] = 'Support Ticket #' . $data['SupportTicket']['id'] . ' - ' . $data['SupportTicket']['title'];
return $data;
}
Copying the code from this method into the Controller gives the same result.
I've found this helpful: https://edivad.wordpress.com/2008/04/15/cakephp-disable-model-queries-caching/
By model:
class Project extends AppModel {
var $cacheQueries = false;
...
By function:
function someFunction {
$this->Model->cacheQueries = false;
...
try using last Insert ID
$id=$this->getLastInsertID();
public function readForView($id)
I'm trying to send an email from AppController in my CakePHP 2.0 app. It works fine if I send it from the PagesController, but I need to be able to send from AppController as well.
I have:
function sendSystemEmail($to = EMAIL_CONTACT, $from = EMAIL_FROM, $subject = null, $body = null, $view = null, $vars = null) {
App::uses('CakeEmail', 'Network/Email');
$email = new CakeEmail();
$email->viewVars(array(
'body' => $body,
'vars' => $vars
));
$email->template($view)
->emailFormat('html')
->from($from)
->to($to)
->subject($subject)
->send();
return;
}
When I use this, I don't get any errors, but the email doesn't arrive. I can't see that there's any different between this and the code I have in PagesController, so I'm assuming that there's something that AppController doesn't have access to maybe? I can't figure out what though!
Sharon, you're not putting the configuration method.. this parameter can put in the constructor of classEmail too, this way..
$email = new CakeEmail('pop3'); //can be smtp or the protocole you are using..
or
$email->config('pop3')
and you need to define the connection here..
./app/Config/email.php
class EmailConfig {
public $pop3 = array(
'transport' => 'Mail',
'from' => 'you#localhost',
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
);
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.