I coded around the problem in the Front Controller, but the same problem is re-appearing in the Module Controllers and it harder to work around here.
Here's how I handled the FrontController problem (actually changed it to FrontDispatcher) the rest of the FrontController will probably be a short procedural script for legacy reasons (the fall back to non-new code needs to includde the target file at the root level of the namespace. not within a function nor class, etc))
Code: Select all
require_once(COMPINABOX.'include/classes/controllers/FrontDispatcher.inc');
require_once(COMPINABOX.'include/classes/controllers/SlidingDoorsAdminController.inc');
Mock::generate("SlidingDoorsAdminController");
Mock::generatePartial("FrontDispatcher","FDTestVersion",
array("_createModuleController"));
class TestFrontDispatcher extends UnitTestCase {
var $savedState;
function TestFrontDispatcher() {
$this->UnitTestCase('TestFrontDispatcher');
}
function setUp() {
$this->savedState=array("Get"=>$_GET,
"Post"=>$_POST,
"Cookie"=>$_COOKIE,
"Server"=>$_SERVER);
$_GET=array();
$_POST=array();
$_SERVER=array();
$_COOKIE=array();
}
function tearDown() {
$_GET=$this->savedState["Get"];
$_POST=$this->savedState["Post"];
$_COOKIE=$this->savedState["Cookie"];
$_SERVER=$this->savedState["Server"];
}
function testInvokeModuleController() {
$mc =& new MockSlidingDoorsAdminController($this);
$mc->expectOnce("invoke");
$_SERVER["REQUEST_URI"]="/register/foo/Admin/bar";
$dispatcher =& new FDTestVersion($this);
$dispatcher->FrontDispatcher();
$dispatcher->setReturnReference("_createModuleController",$mc,
array("SlidingDoorsAdminController"));
$this->assertEqual(1, $dispatcher->dispatch());
$mc->tally();
}
function testFallThrough() {
$mc =& new MockSlidingDoorsAdminController($this);
$mc->expectNever("invoke");
$_SERVER["REQUEST_URI"]="/bar";
$dispatcher =& new FDTestVersion($this);
$dispatcher->FrontDispatcher();
$dispatcher->setReturnReference("_createModuleController",$mc,
array("SlidingDoorsAdminController"));
$this->assertEqual(0, $dispatcher->dispatch());
$mc->tally();
}
}
?>
(Note the use of the partialMock...)
Here's the implemntation code:
Code: Select all
class FrontDispatcher {
var $_mappings=array();
function FrontDispatcher() {
$this->_mappings["|register/[-A-Za-z0-9_]*/Admin|"]="SlidingDoorsAdminController";
}
function _findModuleController() {
foreach($this->_mappings as $pattern=>$controller)
if (preg_match($pattern,$_SERVER["REQUEST_URI"]))
return $controller;
return "";
}
function _createModuleController($className) {
$mc =& new $className();
return $mc;
}
function dispatch() {
$moduleControllerName = $this->_findModuleController();
if ($moduleControllerName!="") {
$mc=& $this->_createModuleController($moduleControllerName);
$mc->invoke();
return 1;
}
else return 0;
}
}
I was able to get away from testing the private method at least. There's still a "bug" in the code, but I don't know how to use TDD to fix it, so I'll probably cheat. _createMockController will need to require_once the controller before instantiating it. Of course it works in the test, because I had to include the file there to mock it.... But in production it hasn't been included yet.....
But overlooking that, I can "assume" that given an appropriate request the SlidingDoorsAdminController gets invoked, so I'm working on that -- after that's one I should have a complete path from Front Controller to actual Handler invocation.
Now I want to hard-code the mappings from Request_URI to pre-sequenced handler chains into the Controller's constructor. (As I feel any sort of meta-data method is overkill for PHP's applicationless memory model) This will involved lots of object creation.... all of which would have to be partially mocked.... yuck.... any other options?