在控制器中对模拟对象使用期望的最佳实践是什么

What is the best practice to use expectations on mocked objects in controllers

我经常想在控制器中模拟出一个组件或对象。我使用 controllerSpy 方法来执行此操作:

public function controllerSpy($event, $controller = null)
{
    parent::controllerSpy($event, $controller);

    $OrderConfirmation = $this->getMockBuilder('OrderConfirmation')
        ->setMethods(['send'])
        ->getMock();
    $OrderConfirmation->expects($this->once())->method('send');
    $this->_controller->OrderConfirmation = $OrderConfirmation;
}

问题是在每次测试中都设置了调用 send-Method 的预期。但是要看场景,也有不发送确认的场景。我想在测试本身而不是在 controllerSpy 方法中全局配置期望。像这样:

public function testAddOrder()
{
    $this->_controller->OrderConfirmation->expects($this->once())->method('send');
    $this->post('/add_order');
}

但她的问题是预期会丢失,因为稍后会覆盖整个 OrderConfirmation 对象。有人解决过这个问题吗?

您可以只收听 Controller.initialize 事件,这也是测试调度程序所做的:

public function testAddOrder()
{
    \Cake\Event\EventManager::instance()->on(
        'Controller.initialize',
        function (\Cake\Event\EventInterface $event) {
            /** @var \Cake\Controller\Controller $controller */
            $controller = $event->getSubject();
            
            // ...

            $controller->OrderConfirmation = $OrderConfirmation;
        }
    );

    $this->post('/add_order');
}

或者为 controllerSpy() 方法实现回调,例如:

protected $controllerSpyCallback;

public function controllerSpy($event, $controller = null)
{
    parent::controllerSpy($event, $controller);

    if (is_callable($this->controllerSpyCallback)) {
        $this->controllerSpyCallback($this->_controller);
    }
}

public function testAddOrder()
{
    $this->controllerSpyCallback = function (\Cake\Controller\Controller $controller) {
        // ...
        
        $controller->OrderConfirmation = $OrderConfirmation;
    };

    $this->post('/add_order');
}