You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ember.js v3.2.x控制器测试调用路由方法报错如何解决?

问题解决办法

错误原因

单元测试环境下控制器未和对应路由绑定,this.send 依赖的控制器内部target属性为undefined,导致调用时触发属性读取错误。控制器的单元测试默认只会初始化控制器本身,不会自动挂载对应的路由上下文。


方案1:Stub send 方法(推荐,符合单元测试单一职责原则)

控制器单元测试只需要验证控制器自身逻辑正确即可,不需要真实触发路由的方法,路由方法的逻辑可以单独在路由单元测试中验证。
修改后的测试代码如下:

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { spy } from 'sinon'; // 如果你的项目没有引入sinon,也可以手动模拟spy

module('Unit | Controller | scan-monitor', function (hooks) {
  setupTest(hooks);

  let scanMonitorController;

  hooks.beforeEach(function () {
    scanMonitorController = this.owner.lookup('controller:scan-monitor');
  });

  test('controllers.scan-monitor.onRefreshClicked', function (assert) {
    // 模拟点击事件,监听preventDefault调用
    const mockClickEvent = new MouseEvent('click', {});
    const preventDefaultSpy = spy(mockClickEvent, 'preventDefault');
    // stub控制器的send方法,不需要真实执行
    const sendStub = spy(scanMonitorController, 'send');

    scanMonitorController.onRefreshClicked(mockClickEvent);

    // 验证控制器逻辑执行符合预期
    assert.true(preventDefaultSpy.calledOnce, '点击事件的默认行为被阻止');
    assert.true(sendStub.calledOnceWith('refreshData'), '正确发送refreshData action');
    assert.ok(scanMonitorController);
  });
});

如果没有引入sinon,也可以手动mock:

// 替换上面的spy部分
let preventDefaultCalled = false;
const mockClickEvent = {
  preventDefault: () => preventDefaultCalled = true
};
let sendCalledWith = null;
scanMonitorController.send = (actionName) => sendCalledWith = actionName;

// 调用方法后验证
assert.true(preventDefaultCalled);
assert.equal(sendCalledWith, 'refreshData');

方案2:手动绑定路由上下文(不推荐,适合需要验证联动逻辑的场景)

如果你确实需要在控制器测试中真实触发路由的refreshData方法,可以手动给控制器的target属性赋值为对应路由实例:

test('controllers.scan-monitor.onRefreshClicked', function (assert) {
  const mockClickEvent = new MouseEvent('click', {});
  // 手动绑定路由到控制器的target属性
  scanMonitorController.target = this.owner.lookup('route:scan-monitor');
  // 可以stub路由的refresh方法避免实际刷新逻辑影响测试
  const refreshStub = spy(scanMonitorController.target, 'refresh');

  scanMonitorController.onRefreshClicked(mockClickEvent);

  assert.true(refreshStub.calledOnce, '路由的refresh方法被调用');
  assert.ok(scanMonitorController);
});

注意:该方案会让控制器单元测试依赖路由实现,不符合单元测试的隔离原则,跨模块的联动逻辑建议放在集成测试或验收测试中验证。


内容的提问来源于stack exchange,提问作者RonPringadi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 22:18:04