CakePHP "with" method in mock object don't Work - cakephp

I'm trying testing my application using CakePHP 2.2 RC1, in the certain action of my controller i need one information of Auth object, in my test i have created an mock object for the Auth component, but when i call the method with my mock object become invalid, when i don't put this everything works fine.
Below the mock object wich dont work
$this->controller->Auth
->staticExpects($this->any())
->method('user')
->with('count_id')
->will($this->returnValue(9));
Thanks for your attention guys.
--
Edit
Above the full code of my test case, this a very simple test.
class TagsControllerTest extends ControllerTestCase {
public function testView(){
$Tags = $this->generate('Tags', array(
'components' => array(
'Session',
'Auth' => array('user')
)
));
$Tags->Auth->staticExpects($this->any())
->method('user')
->with('count_id')
->will($this->returnValue(2));
$result = $this->testAction('/tags/view');
$this->assertEquals($result, 2);
}
}
And the code of my action in the Tag controller, this don't have nothing more (for testing purposes) them a return of user object with count_id as parameter.
public function view(){
return $this->Auth->user('count_id');
}
Running the test I received this message:
Expectation failed for method name is equal to when invoked zero or more times
Parameter 0 for invocation AuthComponent::user(null) does not match expected value.
Failed asserting that null matches expected 'count_id'.

After looking at the AuthComponent code, I think the problem lies in the fact that you're not mocking the entire component or you're not mocking the _getUser() method. Don't forget: the methods you're not mocking are real methods!
If you look at the code you'll see that user() is called by _getUser() which in turn is called by startup().
There are two ways to solve this problem, the first is to mock the entire AuthComponent:
$Tags = $this->generate('Tags', array(
'components' => array(
'Session',
'Auth' /* no methods */
)
));
or mock _getUser() in addition to user():
$Tags = $this->generate('Tags', array(
'components' => array(
'Session',
'Auth' => array('user', '_getUser')
)
));
Hopefully, this should solve your problem.

I was facing the same issue which I could not solve by the solution provided.
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));

Related

Mocking a function using Laravel and PHPUnit

I am using Laravel for some projects, and I am trying to create a test for a function. Here is the function in the my controller:
public function showThoseThings()
{
return parent::httpRequest(
function ($context) {
$context->results['item'] = $this->object->findOrFail(list_id)->getElements;
}
);
}
the "getElements()" function is defined in the model as follow:
public function getElements()
{
return $this->belongsToMany(
'things',
'thing_to_list',
'list_id',
'thing_id'
)
->withPivot('position')
->orderBy('thing_to_list.position');
}
Basically in the backend there are three tables a list table that is a collection of things, then there is a things table that contains multiple things associated to a list then we have the pivot table. How can I mock this with Laravel. Here is what I have been doing:
public function testShowThoseTHingsSuccess()
{
$this->mock->shouldReceive('findOrFail')->with(1)->andReturn($this->mock);
$this->mock->shouldReceive('getElements')->once()->andReturn($this->mock);
$response = $this->call('GET', 'workingURI');
var_dump($response);
$this->assertTrue($response->isOk());
But when running phpunit in the command line I am getting:
"Unknown Error","message":"SQLSTATE[HY000] [1045] Access denied for user... Fails asserting that false is true".
I don't know what your $this->mock->.. method is doing but apparently not mocking the model you want.
Because the error states that something tries to access the database and doesn't have correct credentials.
Also the lines:
$this->mock->shouldReceive('findOrFail')->with(1)->andReturn($this->mock);
$this->mock->shouldReceive('getElements')->once()->andReturn($this->mock);
Makes no sense to me, You create a mock of a model and when the methods findOrFail or getElements are triggered you return the same object.. The method getElements states to me that there should be returned some array with models..
Some more info when you want to use a testing database
When you run unit test with laravel and uses the base test class, then Laravel sets the environment to testing. Make sure that you have the right database configuration set for this environment.
You can use I.E. the following configuration to create a database in memory for testing:
// app/config/testing/database.php
<?php
return array(
'default' => 'sqlite',
'connections' => array(
'sqlite' => array(
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => ''
),
)
);
Don't forget to migrate your DB schema before testing, place the following line in the public function setUp() of the base test class.
Artisan::call('migrate');
More info about simulating the databse: NetTuts testing laravel models

Calling $this->model->create() crashes in controller in cakephp

I am having a problem using the create() method in the cakephp. I have the following code and when I run it in debug it just resumes the program when I try to step through it after the create line.
$this->CompanyDeviceQuestionConcern->create();
$this->CompanyDeviceQuestionConcern->set('source_company_id', $company_id);
$this->CompanyDeviceQuestionConcern->set('user_id', $user['id']);
$this->CompanyDeviceQuestionConcern->set('create_date', date('Y-m-d H:i:s'));
$this->CompanyDeviceQuestionConcern->set('modified_date', date('Y-m-d H:i:s'));
Does anyone know why it crashes when I run the first line?
I am not sure but i think your database refuses the create action because of limitations of some fields(like NOT NULL).
There is a workaround for this:
$newData = array(
'id' => NULL,
'source_company_id' => $company_id,
'user_id' => $user['id'],
'create_date' => date('Y-m-d H:i:s'),
'modified_date' => date('Y-m-d H:i:s')
);
$this->CompanyDeviceQuestionConcern->save($newData);
I found the silly mistake. I forgot to include the model in $uses variable in the class at the top. It is rather inconvenient that it doesn't show any type of error however.
Here is what I added to fix it.
class ReviewsController extends AppController {
var $uses = array('Question', 'CompanyTemplateGroupQuestionConcern');
Also if anyone wants any more information on how to uses works you can look here
http://book.cakephp.org/2.0/en/core-utility-libraries/app.html

What is the right way to put the value for the 'limit' property of the Paginator in CakePHP into a config file

Using CakePHP 1.3 I created a small application which has a FooController. In this FooController I am using the Paginator to deliver the content of Foo to its views.
In the head of the FooController class I customized the preparation of the data from Foo like this:
var $paginate = array(
'limit' => 100,
'order' => array('Order.created' => 'desc'));
Now I would like to put the value for 'limit' into a configuration file.
So I created a file $config['list_length'] = '500'; in the config directory which I load in bootstrap.php. That works fine, I can echo the value of list_length, for example in the beforeFilter() of the class.
But I fail to make the Paginator use that value.
First I tried:
var $paginate = array(
'limit' => Configure::read('list_length'),
'order' => array('Order.created' => 'desc'));
But that fails with the following PHP error:
Parse error: syntax error, unexpected '(', expecting ')' in xyz/app/controllers/foo_controller.php on line 9
Then I tried:
public function beforeFilter() {
parent::beforeFilter();
$paginate['limit'] = Configure::read('list_length');
}
But it would still use the value '100' instead of '500'.
I guess this is a rather trivial problem that really comes from the fact that I am not very proficient with basic PHP concepts. I don't have much opportunity to get to program a little bit in my current job, so I'm really out of practice.
I would very much appreciate any hint on how to do this and even more on what concept I am missing here, so I could read a little bit about it.
You almost got it right;
The reason PHP is giving you the error in your first example is that class properties cannot be set using 'dynamic' values; from the documentation:
This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.
http://www.php.net/manual/en/language.oop5.properties.php
If you need to set a property to a value that is 'dynamic' (e.g. The result of a function), you will need to assign if from within a class 'method'
You tried to do this in your second example, however, properties of a class/object should be accessed using $this->nameOfTheProperty to indicate it is not a 'variable', but a 'property' of the object;
public function beforeFilter() {
parent::beforeFilter();
$this->paginate['limit'] = Configure::read('list_length');
}
Hope this helps

$this->find not working in cakePHP

I have a model with the name Deal class Deal extends AppModel
Now in my controller I call a method in the Deal model called getDeal()
$dealInfo = Deal::getDeal($dealID);
I want the info returned to me but the var_dump displays blank
function getDeal($dealID){
$deal = $this->Deal->find('first', array(
'conditions' =>
'Deal.id' =>$dealID
) ,
'fields' => array(
'Deal.id'
) ,
'recursive' => 1,
));
}
This is the first time I'm working in cakePHP, so this question might sound a bit dumb
When you're just using an id as your find condition you can use CakePHP's dynamic finder methods.
Dynamic finders work like this.
$this->Model->findByModelFieldName();
In your example it would be
$this->findById($dealId, array(
'fields' => array('Deal.id'),
'recursive' => 1
));
I don't know if I'm just being mental, but is this simply because you're not returning anything from getDeal()?
Try adding return $deal; and see if that helps. Your question doesn't state exactly where you're doing the var_dump so I might be well off.
Edit:
As per the discussion with you and 8vius, we've established that this isn't right, and you simply need to change $this->Deal->find() to $this->find() because its being run from the model.
Check your function declaration, $deal_id does not exist and as you can see from the parameter you pass to the function which should me $deal_id and not dealID. So you have a misdeclared function calling find with a variable that does not exist.

CakePHP strange behavior with beforeFilter: I cannot set the variables to the view

Okay, this will require some setup:
I'm working on a method of using nice post title "slugs" in the URL's of my cakePHP powered blog.
For example: /blog/post-title-here instead of /blog/view_post/123.
Since I'm obviously not going to write a new method for every post, I'm trying to be slick and use CakePHP callbacks to emulate the behavior of PHP 5's __call() magic method. For those who do not know, CakePHP's dispatcher checks to see if a method exists and throws a cakePHP error before __call() can be invoked in the controller.
What I've done so far:
In the interest of full disclosure ('cause I have no Idea why I'm having a problem) I've got two routes:
Router::connect('/blog/:action/*', array('controller' => 'blog_posts'));
Router::connect('/blog/*', array('controller' => 'blog_posts'));
These set up an alias for the BlogPostsController so that my url doesn't look like /blog_posts/action
Then in the BlogPostsController:
public function beforeFilter() {
parent::beforeFilter();
if (!in_array($this->params['action'], $this->methods)) {
$this->setAction('single_post', $this->params['action']);
}
}
public function single_post($slug = NULL) {
$post = $this->BlogPost->get_post_by_slug($slug);
$this->set('post', $post);
//$this->render('single_post');
}
The beforeFilter catches actions that do not exist and passes them to my single_post method. single_post grabs the data from the model, and sets a variable $post for the view.
There's also an index method that displays the 10 most recent posts.
Here's the confounding part:
You'll notice that there is a $this->render method that is commented-out above.
When I do not call $this->render('single_post'), the view renders once, but the $post variable is not set.
When I do call $this->render('single_post'), The view renders with the $post variable set, and then renders again with it not set. So in effect I get two full layouts, one after the other, in the same document. One with the content, and one without.
I've tried using a method named single_post and a method named __single_post and both have the same problem. I would prefer the end result to be a method named __single_post so that it cannot be accessed directly with the url /blog/single_post.
Also
I've not yet coded error handling for when the post does not exist (so that when people type random things in the url they don't get the single_post view). I plan on doing that after I figure out this problem.
This doesn't explicitly answer your question, but I'd just forego the whole complexity by solving the problem using only routes:
// Whitelist other public actions in BlogPostsController first,
// so they're not caught by the catch-all slug rule.
// This whitelists BlogPostsController::other() and ::actions(), so
// the URLs /blog/other/foo and /blog/actions/bar still work.
Router::connect('/blog/:action/*',
array('controller' => 'blog_posts'),
array('action' => 'other|actions'));
// Connect all URLs not matching the above, like /blog/my-frist-post,
// to BlogPostsController::single_post($slug). Optionally use RegEx to
// filter slug format.
Router::connect('/blog/:slug',
array('controller' => 'blog_posts', 'action' => 'single_post'),
array('pass' => array('slug') /*, 'slug' => 'regex for slug' */));
Note that the above routes depend on a bug fix only recently, as of the time of this writing, incorporated into Cake (see http://cakephp.lighthouseapp.com/projects/42648/tickets/1197-routing-error-when-using-regex-on-action). See the edit history of this post for a more compatible solution.
As for the single_post method being accessible directly: I won't. Since the /blog/:slug route catches all URLs that start with /blog/, it'll catch /blog/single_post and invoke BlogPostsController::single_post('single_post'). You will then try to find a post with the slug "single_post", which probably won't exist. In that case, you can throw a 404 error:
function single_post($slug) {
$post = $this->BlogPost->get_post_by_slug($slug);
if (!$post) {
$this->cakeError('error404');
}
// business as usual here
}
Error handling: done.

Resources