【问题标题】:Unit testing Zend controllers with mock models and services使用模拟模型和服务对 Zend 控制器进行单元测试
【发布时间】:2026-01-26 09:50:02
【问题描述】:

我已经阅读了很多 Zend 控制器测试教程,但我找不到一个解释如何测试使用模型的控制器并模拟这些模型的教程。

我有以下控制器动作:-

function indexAction(){
  // Get the cache used by the application
  $cache = $this->getCache(); 

  // Get the index service client and model
  $indexServiceClient = new IndexServiceClient($this->getConfig());
  $indexModel = $this->_helper->ModelLoader->load('admin_indexmodel', $cache);
  $indexModel->setIndexServiceClient($indexServiceClient);

  // Load all the indexes
  $indexes = $indexModel->loadIndexes();

  $this->view->assign('indexes', $indexes);
}

目前我有一个非常基本的测试用例:-

public function testIndexActionRoute() {
  $this->dispatch( '/admin/index' );
  $this->assertModule('admin', 'Incorrect module used');
  $this->assertController('index', 'Incorrect controller used');
  $this->assertAction('index', 'Incorrect action used');
}

此测试有效,但它正在调用真实的模型和服务,这有时意味着它会超时并在测试环境中失败。为了正确地对控制器进行单元测试,我需要对 IndexServiceClient 和 IndexModel 进行模拟和期望 - 这是如何完成的?

【问题讨论】:

    标签: unit-testing zend-framework


    【解决方案1】:

    好吧,由于我在这里看到的回复不多,我会尝试添加我的 2 美分(可能有争议)。 下面写的答案是我的 IHMO 并且非常主观(我认为不是很有用,但无论如何我们都会这样做)

    我认为控制器不适合进行单元测试。您的业​​务逻辑层、模型等是可统一的。 控制器与 UI 相连,可以说将系统整合在一起——因此对我来说,它们更适合集成和 UI 测试——这是 Selenium 等软件包的用途。

    在我看来,测试应该足够容易实现,这样测试实现的总努力就足以获得它的回报。连接控制器的所有依赖项在我看来(当然,我的知识有限)有点太多的任务。

    另一种思考方式是 - 控制器中实际发生的事情。同样,IHMO 它应该主要是您的业务逻辑和 UI 之间的粘合层。如果您将大量业务逻辑放入控制器中,则会产生不利影响(例如,它不容易统一......)。

    这当然是各种理论。希望有人可以提供更好的答案,并实际展示如何轻松连接控制器以进行单元测试!

    【讨论】:

    • 与同事讨论后得出了相同的结论。在具有适当模拟的控制器中进行单元测试需要付出很多努力而回报很少。尤其如此,因为我们的模型层完成了大部分工作,并且具有广泛的测试覆盖率,而控制器却非常薄。
    【解决方案2】:

    一位同事提出的一种可能的解决方案是使用 Zend Controller Action Helper 来注入 mock 依赖项。这在理论上应该可行,但我还没有对这种方法进行广泛的测试

    这是一个这样做的例子。

    class Mock_IndexModel_Helper extends Zend_Controller_Action_Helper_Abstract {
    
        private $model;
    
        public function __construct($model) {
            $this->model = $model;
        }   
    
        /**
         * Init hook
         */
        public function init() {            
            $this->getActionController()->setIndexModel( $this->model );
        }
    
    }
    
    
    class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase {
    
        public $bootstrap = BOOTSTRAP;
    
        public function setUp(){
            parent::setUp();
        }
    
    
        /**
        * Test the correct module/controller/action are used
        */
        public function testIndexAction() {                 
            $mockIndexModel = $this->getMock("IndexModel");
    
            Zend_Controller_Action_HelperBroker::addHelper(new Mock_IndexModel_Helper($mockIndexModel));
    
            $this->dispatch( '/admin/index' );
    
            $this->assertModule('admin', 'Incorrect module used');
            $this->assertController('index', 'Incorrect controller used');
            $this->assertAction('index', 'Incorrect action used');
        }    
    }
    

    【讨论】: