CakePHP Unit Testing Gotchas

CakePHP has caused me some grief in the past week. Seemingly simple unit testing tasks were made much harder than they should have been. This post is for anyone else who encounters these problems.
The first relates to the testAction method used to test controllers. When putting a querystring in the url doesn’t cause it to be automatically parsed and split like it normally is when the controller is directly invoked.
For example:

$this->testAction('/testing/post?company=utCompany', array('return' => 'vars')) ;

will result in:

[url] => /testing/post?company=utCompany

While invoking the url directly via the web browser results in:

[url] => Array
(
[url] => testing/post
[company] => utCompany
)

The solution I found is a bit of a hack. There is meant to be a fix in the pipeline but it’s not in my build and I am not ready to update to RC3 (and I’m not even sure if the fix is in RC3). Either way there is no guarantee this feature will work beyond RC2
If the second testAction parameter includes a named array called ‘url’ then the values will be placed in the $this->params object in the controller. This gives us the same net result as when the controller is directly invoked. This is not documented in the CakePHP manual.

$data = array ('company' => 'utCompany') ;

$result = $this->testAction('/testing/post', array
(
'return' => 'vars',
'method' => 'get',
'url' => $data
)) ;

I originally asked this question on StackOverflow but had to answer it myself. Still I got four achievements out of that one question. Not a bad return on my investment.
The second problem relates to caching. It is summed up in this post by Wil Clouser called The Cache that wouldn’t quit. My problem was that I was taking a snapshot of my table before my query ran so I could validate the results. The query had only run just beforehand so CakePHP went to the cache and got my previously unchanged dataset. This one drove me spare. I always forget the cache when I test because my policy is that unless I am testing the cache, the cache is turned off.
The solution is fairly simple. The query command (of which I use a lot rather than CakePHP’s “automagic”) accepts a second parameter called “false”. When supplied it turns caching off.
This still leaves you with a problem because you want to call methods in your model. Your model can’t default to no-cache otherwise your prod code wouldn’t have any caching. The easiest solution I came up with is the following.
All of your model code inherits from AppModel. Update AppModel with the following property.

class AppModel extends Model
{
var $useCache = 'true' ;
}

Then in your test model override the attribute with a value of false.

class UserTest extends User
{
var $name = 'UserTest';
var $useTable = 'users' ;
var $useDbConfig = 'test';
var $useCache = 'false' ;
}

In between those two snippets is your User model. User inherits from AppModel but has no need to manage caching. When you write a method on your model you will need to add one more chunk of code.

$result = parent::query
(
'SELECT
users.id
FROM
users
WHERE
users.name = \'' . $name . '\'
AND
users.suspended = 0',
$this->useCache
) ;

Note that $this->useCache is used as the second parameter. This then gives you the ability to ensure caching is turned off while your test your model.
Normal 0 false false false EN-AU X-NONE X-NONE MicrosoftInternetExplorer4

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Table Normal";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin-top:0cm;
mso-para-margin-right:0cm;
mso-para-margin-bottom:10.0pt;
mso-para-margin-left:0cm;
line-height:115%;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:"Calibri","sans-serif";
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:"Times New Roman";
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;}

Note: Apologies for the crap code highlighting. It isn’t working well. It is better than what it was before, which was self-hiding code…