CakePHP Unit testing controller returning undefined variable - php

I am new to the Unit Testing portion of CakePHP. There is no better time to learn than now.
I have ready through all of the documentation and numerous tutorials on the web. I have run up against a snag during the completion of some of my more basic unit tests.
My Controller:
class TestsController extends AppController {
public $components = array('RequestHandler', 'Security');
public $helpers = array('Js');
public $uses = array('Tests');
public function beforeFilter() {
$this->layout = 'admin';
parent::beforeFilter();
}
/**
* Returns database values of all tests that have been created.
*
*/
public function index() {
if ($this->request->is('requested')) {
return $tests;
}
$this->paginate = array(
'fields' => array('id', 'name', 'email', 'access_token', 'access_token_begins',
'filesizelimit', 'related_files', 'access_token_expires'),
'limit' => 10,
'order' => array(
'created' => 'desc'
));
$this->set('tests', $this->paginate('Test'));
}
My Controller test looks like this:
class TestsControllerTest extends ControllerTestCase {
public $fixtures = array('app.test');
public function testIndex() {
$result = $this->testAction('tests/index');
debug($result);
}
}
The output of my test is:
PHPUNIT_FRAMEWORK_ERROR_NOTICE
Undefined variable: tests
Test case: testsControllerTest(testIndex)
The only viable solution to the error I am receiving would be that maybe the test controller needs authentication before it can actually run the testAction() ?
This is a very simple sample from the CakePHP Documentation guide, yet I receive and undefined variable error. Any help is greatly appreciated.

I have managed to answer my own question.
The $tests variable is undefined because of the if statement within the controller:
public function index() {
if ($this->request->is('requested')) {
return $tests;
}
$this->paginate = array(
'fields' => array('id', 'name', 'email', 'access_token', 'access_token_begins',
'filesizelimit', 'related_files', 'access_token_expires'),
'limit' => 10,
'order' => array(
'created' => 'desc'
));
$this->set('tests', $this->paginate('Test'));
}
That statement does not get hit when running the application, but it does when running the test. In this given function there is no need for the IF statement and therefore my error of UNDEFINED VARIABLE is now resolved.

Related

Zend Framework 2: Create Search Widget for Layout

I have a small private Project to learn ZF2. I have integrated Zend Lucene as a Search Function. This works well, but I now want to integrate a Search Field in my Layout, so that it is available on all pages. I am really not sure how to achieve this. First of all, am I correct that this is done with a View Helper? As you can see below, I have got no idea what I have to enter into the __invoke() of my Helper to display my Search Form. Is my way correct in general or is there a better way? I would like a good ZF2 solution, can someone give me some advice? Thank you very much in advance.
Okay, what have I done so far:
1. I created a Form:
namespace Advert\Form;
use Zend\Form\Form;
class SearchForm extends Form
{
public function __construct()
{
parent::__construct('search');
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'query',
'attributes' => array(
'type' => 'text',
'id' => 'queryText',
'required' => 'required'
),
'options' => array(
'label' => 'Search String',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Search'
),
));
}
}
2. created a View Helper DisplaySearchForm.php !!! 2. UPDATE !!!
A BIG Thank you to AlexP for his help !!!
namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\Form\ElementInterface;
class DisplaySearchForm extends AbstractHelper
{
protected $form;
public function __construct($form)
{
$this->form = $form;
}
public function __invoke($form = null)
{
if ($form) {
$this->form = $form;
}
return $this->render($this->form);
}
public function render($form)
{
// return $this->getView()->form($form);
// To use my own Style, I have added a Partial
return $this->getView()->render('partial/search', array('form' => $form));
}
}
I read somewhere that it would not be good to use the ServiceLocator in the Helper, so I thought about doing that in a Factory, where I will then get the Form. So I created a Factory (not sure the Factory is right)
3. created Factory
namespace Advert\View\Helper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class DisplaySearchFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$realServiceLocator = $serviceLocator->getServiceLocator();
$form = $realServiceLocator->get('FormElementManager')->get('\Advert\Form\SearchForm');
$helper = new DisplaySearchForm($form);
return $helper;
}
}
4. I registered the Factory in the module.php
public function getViewHelperConfig()
{
return array(
'factories' => array(
'displaySearchForm' => 'Advert\View\Helper\DisplaySearchForm',
)
)
}
5. In my Layout layout.phtml
<?php echo $this->displaySearchForm(); ?>
The AbstractHelper has a getView() which returns the 'renderer'. This means you can use all the view helpers you need, as if you were in a view script.
The new helper could look like this.
use Zend\Form\ElementInterface;
class DisplaySearchForm extends AbstractHelper
{
protected $element; // Form element to render
public function __construct(ElementInterface $element)
{
$this->element = $element;
}
public function __invoke($element = null)
{
if ($element) {
$this->element = $element;
}
return $this->render($this->element);
}
public function render(ElementInterface $element)
{
return $this->getView()->form($element);
}
}

CakePHP 2 testing model with no table using mock method for email

I'm attempting to write a test for a model that has no table but sends an email if the data passes validation in CakePHP 2.
To test I want to assert that some data passes validation and would therefore send an email without actually sending one. For this I am attempting to create a mock method for CakeEmail. However, the test is failing because $useDbConfig hasn't be defined for the mock method:-
Undefined property: Mock_MyModel_7a1fb8d0::$useDbConfig
I assume this is an issue with the model not having a table, but cannot see how to resolve it.
My model looks something like (excluding the validation rules):-
<?php
App::uses('CakeEmail', 'Network/Email');
class MyModel extends AppModel {
public $useTable = false;
public function send($data) {
$this->set($data);
if ($this->validates() === false) {
return false;
} else {
$Email = $this->getEmailer();
$Email->from($data['MyModel']['email_from']);
$Email->to($data['MyModel']['email_to']);
$Email->subject($data['MyModel']['subject']);
$Email->send($data['MyModel']['message']);
}
return true;
}
public function getEmailer() {
return new CakeEmail();
}
}
My test is:-
<?php
class MyModel extends CakeTestCase {
public function setUp() {
parent::setUp();
$this->MyModel = ClassRegistry::init('MyModel');
}
public function testSend() {
$emailer = $this->getMock(
'CakeEmail',
array(
'to',
'emailFormat',
'subject',
'replyTo',
'from',
'template',
'viewVars',
'send'
)
);
$emailer->expects($this->any())->method('send')->will($this->returnValue(true));
$MyModel = $this->getMockForModel('MyModel', array('getEmailer'));
$MyModel->expects($this->once())->method('getEmailer')->will($this->returnValue($emailer));
$data = array(
'MyModel' => array(
'email_to' => 'foo#example.com',
'email_from' => 'bar#example.com',
'subject' => 'Foo bar',
'message' => ''
)
);
$result = $MyModel->send($data);
$this->assertTrue($result);
}
}
Any help would be appreciated. This is the first time I've tried/needed to mock a method in Cake using tests.
Class name should have been MyModelTest rather than MyModel. CakePHP's naming convention needs to be adhered to.

Unset child object static variable from abstract parent class php

I am using Laravel and it's Validators.
I have the following code in my controller:
class ResellerController extends BaseController{
public function add() {
//some code before
$userValidator = new App\Services\Validators\UserCreateValidator();
//HERE I WANT TO REMOVE THE company KEY FROM THE RULES IN THE USERS CREATE VALIDATOR
$userValidator->removeRule('company');
//code execution continues
}
}
The UserCreateValidator extends a parent Validator class:
namespace App\Services\Validators;
class UserCreateValidator extends Validator {
public static $rules = array(
'firstName' => 'required',
'lastName' => 'required',
'email' => 'required|email|unique:users',
'company' => 'required'
);
}
And here is the base Validator class:
namespace App\Services\Validators;
abstract class Validator {
/**
* Validation rules
* #var array
*/
public static $rules;
//THIS CODE DOES NOT WORK IN THE CONTROLLER UP
public static function removeRule($ruleKey){
if(is_array($ruleKey))
{
foreach($ruleKey as $key)
{
if(!array_key_exists($key, static::$rules)) continue;
unset(static::$rules[$key]);
}
return true;
}
if(!array_key_exists($ruleKey, static::$rules)) //return false;
unset(static::$rules['company']);
return true;
}
}
The unsettting of the static::$rules[$key] in ResellerController does not work.
I can see in a XDEBUG session (after this line gets executed) that the static::$rules['company'] is still present in the UserCreateValidator as property.
I thought that Late Static Binding should solve this problem?
What is wrong?
The problem is solved. It was in the commented part in the:
if(!array_key_exists($ruleKey, static::$rules)) //return false;
The unsetting is working fine after I uncomment the return false.
Silly mistake :)

Yii disable Model Behavior for one Controller

Im using a behavior(DateTimeI18NBehavior) in Users.php model, but specifically in a controller (ApiController.php) I would like to disable it.
Model - Users.php:
public function behaviors()
{
return array(
'datetimeI18NBehavior'=>array(
'class' => 'ext.DateTimeI18NBehavior',
),
);
}
I know that I can it disable with:
$model->disableBehavior('datetimeI18NBehavior');
But how to disable to entire Controller?
Not sure, but maybe this would work:
class ApiController extends CController
{
function init()
{
Users::model()->disableBehavior('datetimeI18NBehavior');
}
}
Or you can try to add some logic in your model:
function behaviors()
{
if (Yii::app()->controller->uniqueId != 'api') {
return parent::behaviors();
}
return array(
'datetimeI18NBehavior'=>array(
'class' => 'ext.DateTimeI18NBehavior',
),
);
}
Both ways aren't perfect though in my opinion.

Yii CActiveDataProvider with class argument?

I found this code at this website. What would have to be in the $dp dataprovider for the class TotalColumn to be called in the CGridView? Do I have to have the class TotalColumn be somewhere in $dp? Any idea how I would declare that CActiveDataProvider?
<?php
// protected/views/inventory/index.php
Yii::import('zii.widgets.grid.CGridColumn');
class TotalColumn extends CGridColumn {
private $_total = 0;
public function renderDataCellContent($row, $data) { // $row number is ignored
$this->_total += $data->quantity;
echo $this->_total;
}
}
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider' => $dp, // provided by the controller
'columns' => array(
'id',
'name',
'quantity',
array(
'header' => 'Total',
'class' => 'TotalColumn'
)
)));
Here is my code, but nothing in my custom column is displayed:
Yii::import('zii.widgets.grid.CGridColumn');
class TotalSkills extends CGridColumn
{
private $test = "blah";
public function renderSkills($row, $data)
{
echo $this->test;
}
}
// People
echo CHtml::label('People', 'yw0', $shared_html_options);
$dataProvider = new CActiveDataProvider('Person');
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
'columns'=>array(
'name',
'age',
array(
'header'=>'Total Skills',
'class'=>'TotalSkills'
)
)
));
You should create the TotalColumn class inside your protected/components directory as a TotalColumn.php file. That way you can use it in many different view files, instead of the view file that is defined only. Yii will load it automatically then.
$dp should be a typical DataProvider class (more likely a CActiveDataProvider) that is defined in your controller and passed to your view. The DataProvider can be as easy as the CGridView documentation describes it.
public function renderDataCellContent($row, $data)
is defined method in gridview
but there is no method such as
public function renderSkills($row, $data)
in gridview

Categories