Friday, 15 January 2010

Properly Mocking a CakePHP Controller's referer() -



Properly Mocking a CakePHP Controller's referer() -

i'm playing cakephp, building simple blog site has posts , comments. baked models, controllers, , views. when you're viewing post, has "related comments" section @ bottom, showing of post's comments. each comment has action buttons ("view", "edit", , "delete"). noticed if edit or delete comment, not taken post viewing. instead, taken comments index. same thing happens when you're viewing post , click "new comment" button @ bottom , add together new comment post. wanted modify behavior if you're viewing post , edit or delete existing comment or add together new comment, you're redirected post. added code each of these 3 actions ("add", "edit", , "delete") in comments controller. question focuses on "delete" action. did:

original code in app/controller/commentscontroller.php public function delete($id = null) { $this->comment->id = $id; if (!$this->comment->exists()) { throw new notfoundexception(__('invalid comment')); } $this->request->allowmethod('post', 'delete'); if ($this->comment->delete()) { $this->session->setflash(__('the comment has been deleted.')); } else { $this->session->setflash(__('the comment not deleted. please, seek again.')); } homecoming $this->redirect(array('action' => 'index')); } modified code in app/controller/commentscontroller.php public function delete($id = null) { /* * same above minus redirect line (the next code replaces * redirect line). */ // redirect `$this->referer()` unless `$this->referer()` // comment's "edit" or "view" action (we not want // redirect user 404 page), in case redirect them // comments index. $referer = $this->referer(); if ($referer) { $comingfromeditpage = mb_strpos( $referer, router::url(array('action' => 'edit', $id), true) ) === 0; if (!$comingfromeditpage) { $comingfromviewpage = mb_strpos( $referer, router::url(array('action' => 'view', $id), true) ) === 0; } if (!$comingfromeditpage && !$comingfromviewpage) { $redirecturl = $referer; } } if (!isset($redirecturl)) { $redirecturl = array('action' => 'index'); } homecoming $this->redirect($redirecturl); }

i wanted test behavior in app/test/case/controller/commentscontrollertest.php file, made this:

commentscontrollertest.php <?php app::uses('commentscontroller', 'controller'); class commentscontrollertest extends controllertestcase { public $fixtures = array( 'app.comment', 'app.post' ); public function teardown() { parent::teardown(); unset($this->controller); } public function testdeletereferer() { // when referer comment's "edit" action, deleting // comment should redirect comments index. $this->controller = $this->generate('comments', array( 'methods' => array( 'referer' ) )); $this->controller->expects($this->any())->method('referer')->will( $this->returnvalue( configure::read('app.fullbaseurl') . $this->controller->request->base . router::url( array( 'controller' => 'comments', 'action' => 'edit', '1', ) ) ) ); $commentsindexurl = configure::read('app.fullbaseurl') . $this->controller->request->base . router::url( array( 'controller' => 'comments', 'action' => 'index', ) ); $this->testaction( '/comments/delete/1', array('method' => 'post') ); $this->assertequal( $this->headers['location'], $commentsindexurl ); // when referer comment's "view" action, deleting // comment should redirect comments index. $this->controller = $this->generate('comments', array( 'methods' => array( 'referer' ) )); $this->controller->expects($this->any())->method('referer')->will( $this->returnvalue( configure::read('app.fullbaseurl') . $this->controller->request->base . router::url( array( 'controller' => 'comments', 'action' => 'view', '2', ) ) ) ); $this->testaction( '/comments/delete/2', array('method' => 'post') ); // next assertion fails on command line! $this->assertequal( $this->headers['location'], $commentsindexurl ); } }

as stated in code above, sec assertequal fails on command line. when run cake test --stderr app controller/commentscontroller, output:

welcome cakephp v2.5.2 console --------------------------------------------------------------- app : app path: /applications/mamp/htdocs/blogtest/app/ --------------------------------------------------------------- cakephp test shell --------------------------------------------------------------- f time: 0 seconds, memory: 18.50mb there 1 failure: 1) commentscontrollertest::testdeletereferer failed asserting 2 strings equal. --- expected +++ actual @@ @@ -'http://localhost/applications/mamp/htdocs/blogtest/lib/cake/console/comments' +'http://localhost/applications/mamp/htdocs/blogtest/lib/cake/console/applications/mamp/htdocs/blogtest/lib/cake/console/comments/view/2' /applications/mamp/htdocs/blogtest/lib/cake/testsuite/caketestcase.php:552 /applications/mamp/htdocs/blogtest/app/test/case/controller/commentscontrollertest.php:93 /applications/mamp/htdocs/blogtest/lib/cake/testsuite/caketestcase.php:82 /applications/mamp/htdocs/blogtest/lib/cake/testsuite/caketestrunner.php:60 /applications/mamp/htdocs/blogtest/lib/cake/testsuite/caketestsuitecommand.php:96 /applications/mamp/htdocs/blogtest/lib/cake/console/command/testshell.php:274 /applications/mamp/htdocs/blogtest/lib/cake/console/command/testshell.php:259 /applications/mamp/htdocs/blogtest/lib/cake/console/shell.php:440 /applications/mamp/htdocs/blogtest/lib/cake/console/shelldispatcher.php:207 /applications/mamp/htdocs/blogtest/lib/cake/console/shelldispatcher.php:66 failures! tests: 1, assertions: 2, failures: 1.

any thought why happens on command line though test works fine in browser? , how can prepare it?

this doesn't reply exact question believe solves problem faster, cleaner , makes much easier understand , test.

you want redirect index if delete succeeds (so not cause 404) or referrer if fails.

public function delete($id = null) { if ($this->comment->delete($id)) { //if comment deletion succeeds homecoming $this->redirect(array('action' => 'index'); //redirect index } homecoming $this->redirect($this->referer()); //else redirect referrer }

notice how return stop farther execution don't need else. code 3 straightforward lines , 1 indentation level deep.

also think code hardly needs testing since based on heavily tested core functionality. can trust model::delete() or controller::redirect() job , if trust can trust controller method perform intended too. think more of import test models controllers (not meaning controllers should left out bother test things complicated or prone changes/errors).

so if still need go testing extremes can set $_server['http_referer'] whatever (which picked cakerequest $this->referer()) , see if url redirected after delete fails.

cakephp

No comments:

Post a Comment