Fail to mock object (Cakephp 3.0.3) - unit-testing

I am using Cakephp 3.0.3 and I have following method in my User Entity,
public function sendRecovery()
{
$email = new Email('default');
$email->viewVars([
'userId' => $this->id,
'token' => $this->token
]);
$email->template('GintonicCMS.forgot_password')
->emailFormat('html')
->to($this->email)
->from([Configure::read('admin_mail') => Configure::read('site_name')])
->subject('Forgot Password');
return $email->send();
}
i am writing testCase for it.
here is my testCase Method.
public function testSendRecovery()
{
$entity = new User([
'id' => 1,
'email' => 'hitesh#securemetasys.com',
'token' => 'jhfkjd456d4sgdsg'
]);
$email = $this->getMock('Cake\Network\Email\Email', ['sendRecovery']);
$email->expects($this->once())
->method('sendRecovery')
->with($this->equalTo('hitesh#securemetasys.com'));
$entity->sendRecovery();
}
when i run phpunit then i got following error,
There was 1 error:
1) GintonicCMS\Test\TestCase\Model\Entity\UserTest::testSendRecovery
InvalidArgumentException: Unknown email configuration "default".
E:\xampp\htdocs\Cake3\Proball-Market\plugins\GintonicCMS\vendor\cakephp\cakephp\src\Network\Email\Email.php:1382
E:\xampp\htdocs\Cake3\Proball-Market\plugins\GintonicCMS\vendor\cakephp\cakephp\src\Network\Email\Email.php:1269
E:\xampp\htdocs\Cake3\Proball-Market\plugins\GintonicCMS\vendor\cakephp\cakephp\src\Network\Email\Email.php:383
E:\xampp\htdocs\Cake3\Proball-Market\plugins\GintonicCMS\src\Model\Entity\User.php:90
E:\xampp\htdocs\Cake3\Proball-Market\plugins\GintonicCMS\tests\TestCase\Model\Entity\UserTest.php:73
can any one help me Please?

Related

Cakephp 3 use mock to test controller

I want to test a controller using a mock.
In my controller
public function myAction() {
$email = new MandrillApi(['template_name'=>'myTemplate']);
$result = $email
->subject('My title')
->from('no-reply#test.com')
->to('dest#test.com')
->send();
if ( isset($result[0]['status']) && $result[0]['status'] === 'sent' )
return $this->redirect(['action' => 'confirmForgotPassword']);
$this->Flash->error(__("Error"));
}
In test
public function testMyAction() {
$this->get("users/my-action");
$this->assertRedirect(['controller' => 'Users', 'action' => 'confirmForgotPassword']);
}
How do I mock the class MandrillApi ? thank you
In your controller-test:
public function controllerSpy($event){
parent::controllerSpy($event);
if (isset($this->_controller)) {
$MandrillApi = $this->getMock('App\Pathtotheclass\MandrillApi', array('subject', 'from', 'to', 'send'));
$this->_controller->MandrillApi = $MandrillApi;
$result = [
0 => [
'status' => 'sent'
]
];
$this->_controller->MandrillApi
->method('send')
->will($this->returnValue($result));
}
}
The controllerSpy method will insert the mocked object once the controller is setup correctly. You don't have to call the controllerSpy method, it gets executed automatically at some point after you make the $this->get(... call in your test.
Obviously you have to change the App\Pathtotheclass-part of the mock-generation to fit the location of your MandrillApi-class.

Cakephp mock Email Utility

i'm a little stuck trying to test my users-controller in cakephp,
i have an action wich sends an email to a certain email address.
The email utility works, no problem whatsoever.
I want to mock the email utility so that when i test the action (with "testAction"),
no email will be sent.
I already searched all over stackoverflow and tried a lot of solutions, the current code is like follows:
UsersController:
/**
* Get Email Utility, use method so that unit testing is possible
* #return object
*/
public function _getEmailer()
{
return new CakeEmail();
}
public function lostPassword()
{
if($this->request->is('post'))
{
$email = $this->request->data['User']['email'];
$this->User->recursive = -1;
$user = $this->User->find('first', array('conditions' => array('email' => $email)));
if(!$user)
{
$this->Session->setFlash('Error: user not found');
return $this->render();
}
$recoverTrials = $this->User->Recover->find('count', array('conditions' => array('email' => $email)));
if($recoverTrials > 3)
{
$this->Session->setFlash(__('message.recover-too-many'));
return $this->redirect('/');
}
// Generate random key to reset password
$key = md5(microtime().rand());
$data = array('user_id' => $user['User']['id'],
'key' => $key,
'active' => 1,
'created' => date('Y-m-d H:i:s'));
if(!$this->User->Recover->save($data))
{
$this->Session->setFlash('Error while sending the recovery-mail');
return $this->redirect('/');
}
$this->Email = $this->_getEmailer();
$this->Email->emailFormat('html');
$this->Email->to($email);
$this->Email->subject('Password recover');
$this->Email->replyTo('noreply#domain.com');
$this->Email->from(array('noreply#domain.com' => 'Sender ID'));
$this->Email->template('recover');
$this->Email->viewVars(array('key' => $key)); // Set variables for template
try
{
$this->Email->send();
}
catch(Exception $e)
{
$this->Session->setFlash('Error while sending the recovery-mail');
return $this->render();
}
$this->Session->setFlash('The recovery mail with instructions to reset your password has been sent. Please note that the link will only remain active for 2 hours.');
return $this->redirect('/');
}
}
And my test class looks like (excerpt):
public function testPostLostPassword()
{
$this->Controller = $this->generate('Users', array(
'methods' => array(
'_getEmailer'
),
'components' => array('Security')
));
$emailer = $this->getMock('CakeEmail', array(
'to',
'emailFormat',
'subject',
'replyTo',
'from',
'template',
'viewVars',
'send'
));
$emailer->expects($this->any())
->method('send')
->will($this->returnValue(true));
$this->Controller->expects($this->any())
->method('_getEmailer')
->will($this->returnValue($emailer));
Correct email
$data = array('User' => array('email' => 'me#domain.com'));
$result = $this->testAction('/lostPassword', array('method' => 'post',
'data' => $data,
'return' => 'contents'));
}
What am i doing wrong? The Email utility still sends out the email to my address, even though i mocked it...
Thanks in advance!

Accessing Model in CakePHP Controller Test

I'm new to CakePHP, and I just started writing my first tests. Usually doing Ruby on Rails, my approach to testing a Controller::create action would be to call the create action, and then comparing the number of models before and after that call, making sure it increased by one.
Would anyone test this any other way?
Is there an easy (builtin) way to access models from a ControllerTest in CakePHP? I couldn't find anything in the source, and accessing it through the Controller seems wrong.
I ended up doing something like this:
class AbstractControllerTestCase extends ControllerTestCase {
/**
* Load models, to be used like $this->DummyModel->[...]
* #param array
*/
public function loadModels() {
$models = func_get_args();
foreach ($models as $modelClass) {
$name = $modelClass . 'Model';
if(!isset($this->{$name})) {
$this->{$name} = ClassRegistry::init(array(
'class' => $modelClass, 'alias' => $modelClass
));
}
}
}
}
Then my tests inherit from AbstractControllerTestCase, call $this->loadModels('User'); in setUp and can do something like this in the test:
$countBefore = $this->UserModel->find('count');
// call the action with POST params
$countAfter = $this->UserModel->find('count');
$this->assertEquals($countAfter, $countBefore + 1);
Note that I'm new to CakePHP but came here with this question. Here's what I ended up doing.
I got my idea from #amiuhle, but I just do it manually in setUp, like how they mention in the model tests at http://book.cakephp.org/2.0/en/development/testing.html.
public function setUp() {
$this->Signup = ClassRegistry::init('Signup');
}
public function testMyTestXYZ() {
$data = array('first_name' => 'name');
$countBefore = $this->Signup->find('count');
$result = $this->testAction('/signups/add',
array(
'data' => array(
'Signup' => $data)
)
);
$countAfter = $this->Signup->find('count');
$this->assertEquals($countAfter, $countBefore + 1);
}
I am not sure why it is necessary to test how many times a model is called or instantiated from the controller action.
So, if I was testing Controller::create... my ControllerTest would contain something like:
testCreate(){
$result = $this->testAction('/controller/create');
if(!strpos($result,'form')){
$this->assertFalse(true);
}
$data = array(
'Article' => array(
'user_id' => 1,
'published' => 1,
'slug' => 'new-article',
'title' => 'New Article',
'body' => 'New Body'
)
);
$result = $this->testAction(
'/controller/create',
array('data' => $data, 'method' => 'post')
);
if(!strpos($result,'Record has been successfully created')){
$this->assertFalse(true);
}
}
The main things you want to test for is whether you are getting the right output for the input. And you can use xDebug profiler to easily find out what classes get instnantiated in a particular action and even how many times. There is no need to test for that manually!

how to test Add when saving Associates Cakephp

how to test such a method:
public function add() {
if (!empty($this->request->data)) {
$this->Contest->create();
if ($this->Contest->saveAll($this->request->data)) {
$contestStage['name'] = 'First - ' . $this->request->data['Contest']['name'];
$contestStage['contest_id'] = $this->Contest->id;
if ($this->Contest->ContestStage->save($contestStage)) {
$this->setMessage(__ADD_OK, 'Konkurs');
$this->redirect(array(
'action' => 'view',
$this->Contest->id
));
} else {
$this->setMessage(__ADD_ERROR, 'Konkurs');
}
} else {
$this->setMessage(__ADD_ERROR, 'Konkurs');
}
}
}
my test method:
public function testAdd() {
$this->generateWithAuth(self::ADMIN); // genereting controller here
$url = $this->getUrl('add');
$options2 = array(
'method' => 'post',
'data' => array(
'Contest' => array(
'id' => 3,
'owner_id' => 1,
'name' => 'Testing',
'created' => '2012-11-16 12:02:33.946',
),
),
);
$this->testAction($url, $options2);
$this->assertArrayHasKey('Location', $this->headers, 'No redirection');
$this->assertEquals($this->Contest->hasAny(array('Contest.name' => 'Testing')), true);
$messages = Set::extract('{flash}.message', CakeSession::read('Message'));
}
what i receive is
PDOEXCEPTION
SQLSTATE[23505]: Unique violation: 7 BŁĄD: double key value violates a constraint
     uniqueness "contest_stages_pkey" DETAIL: Key (id)=(1) alredy exists.
Because it's true i have a contestStage with id=1
why its not using next one ;<
Its kinda strange that Cakephp does not document how to test that well.
The problem is that your inserting the Contest id twice. You should make sure that the db that your using has a test prefix (or whatever you like) and clear the test tables.
As an alternative, you could use fixtures instead. The data produces a much better test case as it pre populates the data for you so that you know whats in the db at any time. Still make sure to use a prefix, I made the mistake once of not doing that and it blew away my entire db every time
Good luck

CakePHP “with” method in mock object returns a null value

I am facing the exact problem as this question.
CakePHP "with" method in mock object don't Work
But the answer provided is not working for me either.
Here's the issue.
Controller test case code:
public function testCreate() {
$batches = $this->generate('ShippingBatches', array(
'components' => array(
'Session',
'Auth' => array('user', '_getUser')
)
));
$batches->Auth->expects($this->once())->method('user')
->with('id')
->will($this->returnValue(1));
$batches->Session
->expects($this->once())
->method('setFlash');
$this->testAction('/shippingbatches/create', array('data' => '[3,6]', 'method' => 'post'));
}
Controller Code:
public function create(){
//Some code here
$d['ShippingBatch']['user_id'] = $this->Auth->user('id');
$this->ShippingBatch->save($d);
//some code here
}
The error:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null
Test case: ShippingBatchesControllerTestCase(testCreate)
The solution is to use staticExpects() instead of expects() as user is a static function.
$batches->Auth->staticExpects($this->once())->method('user')
->with('id')
->will($this->returnValue(1));