CakePHP Controller Testing: Mocking the Auth Component -


the situation

controller code

<?php app::uses('appcontroller', 'controller');  class postscontroller extends appcontroller {      public function isauthorized() {         return true;     }      public function edit($id = null) {         $this->autorender = false;          if (!$this->post->exists($id)) {             throw new notfoundexception(__('invalid post'));         }          if ($this->post->find('first', array(             'conditions' => array(                 'post.id' => $id,                 'post.user_id' => $this->auth->user('id')             )         ))) {             echo 'username: ' . $this->auth->user('username') . '<br>';             echo 'created: ' . $this->auth->user('created') . '<br>';             echo 'modified: ' . $this->auth->user('modified') . '<br>';             echo 'all:';             pr($this->auth->user());             echo 'modified: ' . $this->auth->user('modified') . '<br>';         } else {             echo 'unauthorized.';         }     } } 

output browser

username: admin created: 2013-05-08 00:00:00 modified: 2013-05-08 00:00:00 all:  array (     [id] => 1     [username] => admin     [created] => 2013-05-08 00:00:00     [modified] => 2013-05-08 00:00:00 )  modified: 2013-05-08 00:00:00 

test code

<?php app::uses('postscontroller', 'controller');  class postscontrollertest extends controllertestcase {      public $fixtures = array(         'app.post',         'app.user'     );      public function testedit() {         $this->controller = $this->generate('posts', array(             'components' => array(                 'auth' => array('user')             )         ));          $this->controller->auth->staticexpects($this->at(0))->method('user')->with('id')->will($this->returnvalue(1));         $this->controller->auth->staticexpects($this->at(1))->method('user')->with('username')->will($this->returnvalue('admin'));         $this->controller->auth->staticexpects($this->at(2))->method('user')->with('created')->will($this->returnvalue('2013-05-08 00:00:00'));         $this->controller->auth->staticexpects($this->at(3))->method('user')->with('modified')->will($this->returnvalue('2013-05-08 00:00:00'));         $this->controller->auth->staticexpects($this->at(4))->method('user')->will($this->returnvalue(array(             'id' => 1,             'username' => 'admin',             'created' => '2013-05-08 00:00:00',             'modified' => '2013-05-08 00:00:00'         )));          $this->testaction('/posts/edit/1', array('method' => 'get'));     } } 

output test

username: admin created: 2013-05-08 00:00:00 modified: 2013-05-08 00:00:00 all:  array (     [id] => 1     [username] => admin     [created] => 2013-05-08 00:00:00     [modified] => 2013-05-08 00:00:00 )  modified:  

the problem

there 3 problems here:

  1. the test code repetitive.
  2. the second "modified" line in output test blank. should "2013-05-08 00:00:00" in output browser.
  3. if modify controller code, adding line said echo 'email: ' . $this->auth->user('email') . '<br>'; (just example) between echoing of "username" , "created", test fail error: expectation failed method name equal <string:user> when invoked @ sequence index 2. makes sense since $this->at(1) no longer true.

my question

how can mock auth component in way (1) not repetitive, (2) causes test output same thing browser, , (3) allows me add $this->auth->user('foo') code anywhere without breaking tests?

before answer have admit i've no experience of using cakephp framework. however, have fair amount of experience working phpunit in conjunction symfony framework , have encountered similar issues. address points:

  1. see answer point 3

  2. the reason need additional ...->staticexpects($this->at(5))... statement cover 6th call auth->user(). these statements not define values return call auth->user() specified value. define e.g. 2nd call auth object must method user() parameter 'username' in case 'admin' returned. however, should no longer issue if follow approach in next point.

  3. i making assumption trying achieve here test controller independently of auth component (because frankly doesn't make sense test controller makes series of getter calls on user object) . in case mock object set stub return particular set of results, rather expect specific series of calls particular parameters (see php manual entry on stubs). could done replacing '$this->at(x)' '$this->any()' in code. however, whilst negate need add line mentioned in point 2, you'd still have repetition. following tdd approach of writing tests before code, i'd suggest following:

    public function testedit() {     $this->controller = $this->generate('posts', array(         'components' => array(             'auth' => array('user')         )     ));         $this->controller->auth             ->staticexpects($this->any())             ->method('user')             ->will($this->returnvalue(array(                 'id' => 1,                 'username' => 'admin',                 'created' => '2013-05-08 00:00:00',                 'modified' => '2013-05-08 00:00:00',                 'email' => 'me@me.com',             )));      $this->testaction('/posts/edit/1', array('method' => 'get')); } 

this allow controller updated make many calls user attributes in order provided returned mock object. mock object written return user attributes (or perhaps relevant controller) regardless of whether , how controller retrieves them. (note in specific example if mock contains 'email' pr() statement in controller output different results test browser presuming don't expect able add new attributes record without having update tests).

writing test way means controller edit function need - more testable version:

$this->autorender = false;  if (!$this->post->exists($id)) {     throw new notfoundexception(__('invalid post')); }  $user = $this->auth->user();  if ($this->post->find('first', array(     'conditions' => array(         'post.id' => $id,         'post.user_id' => hash::get($user, 'id')     ) ))) {     echo 'username: ' . hash::get($user, 'username') . '<br>';     echo 'created: ' . hash::get($user, 'created') . '<br>';     echo 'modified: ' . hash::get($user, 'modified') . '<br>';     echo 'all:';     pr($user);     echo 'modified: ' . hash::get($user, 'modified') . '<br>'; } else {     echo 'unauthorized.'; } 

as far can gather, hash::get($record, $key) correct cakephp way retrieve attributes record although simple attributes have here presume user[$key] work well.


Comments

Popular posts from this blog

java - Jmockit String final length method mocking Issue -

asp.net - Razor Page Hosted on IIS 6 Fails Every Morning -

c++ - wxwidget compiling on windows command prompt -