title: How to inject mock classes into controllers (without having the controller aware of the tests)
body: <p>This is a follow-on from a previous question I had: <a href="https://stackoverflow.com/questions/6222001/how-to-decouple-my-data-layer-better-and-restrict-the-scope-of-my-unit-tests">How to decouple my data layer better and restrict the scope of my unit tests?</a></p>
  
  <p>I've read around on Zend and DI/IoC and came up with the following changes to my code:</p>
  
  <p><strong>Module Bootstrap</strong></p>
  
  <pre><code>class Api_Bootstrap extends Zend_Application_Module_Bootstrap
  {
      protected function _initAllowedMethods()
      {
          $front = Zend_Controller_Front::getInstance();
          $front-&gt;setParam('api_allowedMethods', array('POST'));
      }
  
      protected function _initResourceLoader()
      {
          $resourceLoader = $this-&gt;getResourceLoader();
          $resourceLoader-&gt;addResourceType('actionhelper', 'controllers/helpers', 'Controller_Action_Helper');
      }
  
      protected function _initActionHelpers()
      {
          Zend_Controller_Action_HelperBroker::addHelper(new Api_Controller_Action_Helper_Model());
      }
  }
  </code></pre>
  
  <p><strong>Action Helper</strong></p>
  
  <pre><code>class Api_Controller_Action_Helper_Model extends Zend_Controller_Action_Helper_Abstract
  {
      public function preDispatch()
      {
          if ($this-&gt;_actionController-&gt;getRequest()-&gt;getModuleName() != 'api') {
              return;
          }
  
          $this-&gt;_actionController-&gt;addMapper('account', new Application_Model_Mapper_Account());
          $this-&gt;_actionController-&gt;addMapper('product', new Application_Model_Mapper_Product());
          $this-&gt;_actionController-&gt;addMapper('subscription', new Application_Model_Mapper_Subscription());
      }
  }
  </code></pre>
  
  <p><strong>Controller</strong></p>
  
  <pre><code>class Api_AuthController extends AMH_Controller
  {
      protected $_mappers = array();
  
      public function addMapper($name, $mapper)
      {
          $this-&gt;_mappers[$name] = $mapper;
      }
  
      public function validateUserAction()
      {
          // stuff
  
          $accounts = $this-&gt;_mappers['account']-&gt;find(array('username' =&gt; $username, 'password' =&gt; $password));
  
          // stuff
      }
  }
  </code></pre>
  
  <p>So, now, the controller doesn't care what specific classes the mappers are - so long as there is a mapper...</p>
  
  <p>But <strong>how do I now replace those classes with mocks for unit-testing without making the application/controller aware that it is being tested?</strong> All I can think of is putting something in the action helper to detect the current application enviroment and load the mocks directly:</p>
  
  <pre><code>class Api_Controller_Action_Helper_Model extends Zend_Controller_Action_Helper_Abstract
  {
      public function preDispatch()
      {
          if ($this-&gt;_actionController-&gt;getRequest()-&gt;getModuleName() != 'api') {
              return;
          }
  
          if (APPLICATION_ENV != 'testing') {
              $this-&gt;_actionController-&gt;addMapper('account', new Application_Model_Mapper_Account());
              $this-&gt;_actionController-&gt;addMapper('product', new Application_Model_Mapper_Product());
              $this-&gt;_actionController-&gt;addMapper('subscription', new Application_Model_Mapper_Subscription());
          } else {
              $this-&gt;_actionController-&gt;addMapper('account', new Application_Model_Mapper_AccountMock());
              $this-&gt;_actionController-&gt;addMapper('product', new Application_Model_Mapper_ProductMock());
              $this-&gt;_actionController-&gt;addMapper('subscription', new Application_Model_Mapper_SubscriptionMock());
          }
      }
  }
  </code></pre>
  
  <p>This just seems wrong...</p>