I am following Mark Story's "Testing CakePHP controllers - Mock Objects edition" to test some controller actions that have redirects. I am currently mocking out the redirect() method in simple test. I wanted to refactor some code to look as follows:
if (!$this->Model->someGuardCondition($id)){
$this->redirect($this->referer());
}
if ($this->Model->save($this->data)){
$this->redirect(array('controller' => 'controllerName', 'action' => 'actionName', $id));
}
The problem is that if the first condition is true then I do not want the test to ever execute the second condition as this is how it would work in production. Is there a way to cause the test to end whenever redirect() is called without killing the entire test suite? I know I could put a return statement after every redirect but I would rather not modify the code just to get the tests to behave correctly. Should I just stick to using a bunch of ugly nested if/else statements?
EDIT 20120215 I will try make my example code more complete to demonstrate the problem:
some_controller.php:
class SomeController extends AppController
function some_action($id) {
if (!$this->Model->hasAuthorityToDoThis($id)){
$this->Session->setFlash(__('The id is invalid for some reason'));
$this->redirect($this->referer());
}
if ($this->Model->save($this->data)){
$this->Session->setFlash(__('Save successful'));
$this->redirect(array('controller' => 'controllerName', 'action' => 'actionName', $id));
}
}
}
some_controller_test.php:
class SomeControllerTestCase extends AppCakeTestCase {
function testSomeAction() {
$this->SomeController->Session = new MockSessionComponent();
$this->SomeController->Session->expectOnce('setFlash');
$this->SomeController->some_action('some-invalid-id'); // Note this is an invalid ID.
}
}
So you can see from above, I am testing the action with an invalid ID. This means the first condition will pass. It will set the flash message as 'The id is invalid for some reason' and redirect to the referer. In CakePHP redirect will exit the script when calling a redirect so the code would never actually make it to the model->save(). If I mock out redirect() in test however, the method essentially just returns with no effect and the controller continues to run past the point it should. This is good in that it does not abort the test suite but it is bad because it does not actually reflect the bahavior of the actual code.
Say that the call to save $this->data is good. Then both the first and second conditions will pass. This will cause the test to fail because you should have a flash message set only once.
I am guessing there is no way to easily write code like this and it would be better to just stick with nested if/else blocks. I was just trying to reduce cyclomatic complexity.