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
相关产品推荐
相关产品推荐

