控制器中$rootScope.$on注销函数监视失败,如何排查?
问题排查与解决方案
这种场景我之前在AngularJS项目里踩过坑,测试提示spy没被调用,通常是测试时机不对或者没有正确触发scope销毁流程导致的,咱们一步步来解决:
1. 你可能在控制器实例化后才创建了Spy
如果你的测试是先初始化控制器,再去监听$rootScope.$on或者mock注销函数,那控制器里拿到的deregister是原始函数,不是被spy包装的版本——这时候你的spy根本没关联到实际执行的函数上,测试肯定失败。
正确的做法:先Mock再实例化控制器
在创建控制器之前,先给$rootScope.$on创建spy,并让它返回一个你要监视的注销函数:
beforeEach(module('yourAppModule')); let $rootScope, $scope, deregisterSpy; beforeEach(inject(function(_$rootScope_, $controller) { $rootScope = _$rootScope_; $scope = $rootScope.$new(); // 1. 先创建要监视的注销函数spy deregisterSpy = jasmine.createSpy('stateChangeDeregister'); // 2. 让$rootScope.$on返回这个spy spyOn($rootScope, '$on').and.returnValue(deregisterSpy); // 3. 最后再实例化控制器 $controller('YourTargetController', { $rootScope: $rootScope, $scope: $scope }); }));
2. 忘记手动触发$scope的销毁事件
AngularJS在浏览器里会自动处理scope的销毁,但测试环境是孤立的,你必须手动调用$scope.$destroy()来触发销毁事件——否则绑定在$destroy上的注销函数根本不会执行。
测试用例里一定要加这一步
it('should deregister the state change listener when scope is destroyed', function() { // 手动触发scope销毁 $scope.$destroy(); // 现在再断言就会通过了 expect(deregisterSpy).toHaveBeenCalled(); });
3. 最后检查控制器里的绑定逻辑
确认控制器里的注销函数确实正确绑定到了$scope.$destroy事件上,别写错事件名(比如把$destroy写成$destory这种低级错误):
// 控制器里的正确写法示例 angular.module('yourAppModule') .controller('YourTargetController', function($rootScope, $scope) { // 监听$stateChangeStart,拿到注销函数 const deregisterStateListener = $rootScope.$on('$stateChangeStart', function(event, toState) { // 你的业务逻辑 }); // 绑定到scope销毁事件,自动注销监听 $scope.$on('$destroy', deregisterStateListener); });
按照这三步排查,基本就能解决测试断言失败的问题了。
内容的提问来源于stack exchange,提问作者riegersn
相关产品推荐
相关产品推荐

