Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write unit test for a controller that uses AuthComponent in CakePHP 2

I am trying to test a controller action that allows edition of user profiles. Among other things I want to test that every logged user can only edit its own profile and not other's. In case of breaking this restriction the action must redirect to a predefined home page.

With this scenario, I have a fixture that creates a user with ID = 1. So I was thinking on testing the restriction this way:

$data = $this->Users->User->read(null, 1); 
$this->Users->Auth->login($data); 
$this->testAction('/users/edit/2', array('method' => 'get')); 
$url = parse_url($this->headers['Location']); 
$this->assertEquals($url['path'], '/homepage'); 

The test passes this assert. So the next step is to check if executing '/users/edit/1', which has the ID of the logged user, shows the form:

$this->testAction('/users/edit/1', array('method' => 'get', 'return' => 'vars'));
$matcher = array( 
  'tag' => 'form', 
  'ancestor' => array('tag' => 'div'), 
  'descendant' => array('tag' => 'fieldset'), 
); 
$this->assertTag($matcher, $this->vars['content_for_layout'], 'The edition form was not found');

However this assert fails. After digging around with debug() I've found that $this->Auth->user() returns the whole information but $this->Auth->user('id') returns null. Since I use the latter in a comparison within the action, it evaluates as false and causes the test to fail.

The curious thing is that it happens when testing but not when executing the action in a browser. So, what's the correct way of testing this action?

Thanks!

like image 442
elitalon Avatar asked Nov 21 '11 18:11

elitalon


1 Answers

The actual correct answer should be using mock objects instead of actually login the user in manually:

$this->controller = $this->generate('Users', array(
    'components' => array('Auth' => array('user')) //We mock the Auth Component here
));
$this->controller->Auth->staticExpects($this->once())->method('user') //The method user()
    ->with('id') //Will be called with first param 'id'
    ->will($this->returnValue(2)) //And will return something for me
$this->testAction('/users/edit/2', array('method' => 'get')); 

Using mocks is the most easy way to test a controller, and also the most flexible one

Update 11 March 2015

You can also mock all method of AuthComponent

$this->controller = $this->generate('Users', array(
    'components' => array('Auth') // Mock all Auth methods
));
like image 137
José Lorenzo Rodríguez Avatar answered Oct 12 '22 08:10

José Lorenzo Rodríguez