如何Mock AngularJS模块配置中的$stateProvider自定义方法
AngularJS测试中Mock $stateProvider自定义扩展方法的解决方案
问题背景
我们的AngularJS模块代码如下:
(function () { 'use strict'; angular .module('someModule', []) .config(config); config.$inject = ['$stateProvider']; function config ($stateProvider) { $stateProvider .appState({}) } })();
其中appState是为$stateProvider扩展的自定义方法,该方法来自外部项目,仅在运行时加载。
测试时,我们尝试在spec文件中覆写该方法:
beforeEach(function () { angular.mock.module('someModule', function ($stateProvider) { spyOn($stateProvider, 'appState').and.returnValue({ stateName: 'mockState', url: '/mock-url' }); }); });
但无论如何尝试,原方法始终被调用,无法覆写。若不提前Mock该方法,模块初始化会报错:
Error: [$injector:modulerr] Failed to instantiate module someModule due to: TypeError: $stateProvider.appState is not a function
请问如何Mock ui-router的$stateProvider这类AngularJS模块配置提供者?
解决方案
核心问题在于:模块的config块会在你尝试在angular.mock.module的配置函数中Mock之前就执行,所以你的Spy根本没来得及生效;而如果提前不定义appState,模块初始化时又会因为找不到方法报错。
正确的做法是在加载目标模块之前,先给$stateProvider添加Mock的appState方法,以下是两种可行方案:
方案一:分步Mock与模块加载
beforeEach(function () { // 1. 先创建临时模块,给$stateProvider添加基础的appState方法 angular.module('mockStateExt', []) .config(function ($stateProvider) { // 实现链式调用的基础方法,避免模块初始化报错 $stateProvider.appState = function() { return $stateProvider; }; }); // 2. 先加载临时Mock模块,再加载目标业务模块 angular.mock.module('mockStateExt', 'someModule'); // 3. 模块加载完成后,替换为带监听的Spy inject(function ($stateProvider) { spyOn($stateProvider, 'appState').and.returnValue({ stateName: 'mockState', url: '/mock-url' }); }); });
方案二:简化前置Mock写法
如果不需要精细监听调用,仅需让模块初始化通过,可直接在加载目标模块前完成Mock:
beforeEach(function () { // 先执行Mock配置,确保appState在模块config前存在 angular.mock.module(function ($stateProvider) { $stateProvider.appState = jasmine.createSpy('appState').and.returnValue($stateProvider); }); // 再加载目标模块 angular.mock.module('someModule'); });
原理说明
AngularJS的模块加载按顺序执行,只要保证$stateProvider.appState方法在目标模块的config块执行前被定义,就能避免初始化报错;后续再替换为Spy则可以捕获方法调用并返回自定义值。
内容的提问来源于stack exchange,提问作者OrangeJuice1-
相关产品推荐
相关产品推荐

