无ngApp指令手动引导的Angular应用,Protractor addMockModule无法生效?
问题根源分析
你的问题核心在于Protractor的addMockModule是为Angular自动引导(通过ngApp指令)设计的,而手动引导的场景下,这个机制无法自动把mock模块注入到你的应用依赖中。
你看到inside httpBackendMock被打印,说明Protractor确实执行了mock模块的定义函数,但这个模块并没有被你的Angular应用引用——因为手动引导时,你是通过angular.bootstrap手动指定应用依赖模块的,而这些mock模块不在这个列表里,所以模块的run块永远不会被触发,自然无法拦截API请求。
解决方案
针对手动引导的Angular应用,我们需要让mock模块被包含到应用的依赖列表中,这里有两种可靠的实现方式:
方式一:在应用代码中预留测试模块注入入口
修改你的应用手动引导代码,增加一个测试环境下的入口,允许外部传入额外的mock模块:
// 你的应用手动引导代码(比如app.js) var baseModules = ['myApp']; // 这里是你原本的应用主模块 // 仅在测试环境下加载额外mock模块 if (window.__PROTRACTOR_MOCK_MODULES__) { baseModules = baseModules.concat(window.__PROTRACTOR_MOCK_MODULES__); } // 手动引导应用 angular.bootstrap(document.body, baseModules);
然后在你的Protractor测试代码中,先把需要加载的mock模块名存入全局window对象,再加载页面:
var angularMocks = require('./angularMocks.module.js').mock; browser.addMockModule("ngMockE2E", angularMocks ); browser.addMockModule('httpBackendMock', function httpBackendMock() { console.log('inside httpBackendMock'); angular.module("httpBackendMock", ['ngMockE2E']) .run(['$httpBackend', function ($httpBackend) { console.log("Inside Run"); $httpBackend.whenGET(/.*employees.*/).respond(404, ''); $httpBackend.whenGET(/.*/).passThrough(); $httpBackend.whenPOST(/.*/).passThrough(); $httpBackend.whenPUT(/.*/).passThrough(); }]); }); // 在加载页面前,将mock模块名注入到全局变量 browser.executeScript(() => { window.__PROTRACTOR_MOCK_MODULES__ = ['ngMockE2E', 'httpBackendMock']; }); browser.get(url);
这种方式的好处是不需要大幅改动测试代码,且生产环境下因为window.__PROTRACTOR_MOCK_MODULES__不存在,不会有任何影响。
方式二:在Protractor中手动引导应用并包含mock模块
如果不想修改应用代码,可以在Protractor中控制页面加载时机,手动完成应用的bootstrap,同时包含mock模块:
// 先加载页面(确保你的应用代码此时不会自动执行bootstrap,比如可以把bootstrap代码放到一个函数里,等待触发) browser.get(url); // 在浏览器上下文执行脚本,手动加载mock模块并引导应用 browser.executeAsyncScript((callback) => { // 加载Angular Mocks脚本(如果之前的addMockModule没生效,这里直接引入) const mockScript = document.createElement('script'); mockScript.src = './angularMocks.module.js'; // 注意路径要和测试环境的资源路径匹配 mockScript.onload = () => { // 定义httpBackendMock模块 angular.module("httpBackendMock", ['ngMockE2E']) .run(['$httpBackend', ($httpBackend) => { console.log("Inside Run"); $httpBackend.whenGET(/.*employees.*/).respond(404, ''); $httpBackend.whenGET(/.*/).passThrough(); $httpBackend.whenPOST(/.*/).passThrough(); $httpBackend.whenPUT(/.*/).passThrough(); }]); // 手动引导应用,包含主模块和mock模块 angular.bootstrap(document.body, ['myApp', 'ngMockE2E', 'httpBackendMock']); callback(); }; document.body.appendChild(mockScript); });
这种方式适合无法修改应用代码的场景,但需要确保应用代码没有提前自动执行bootstrap。
关键注意点
- 确保
angularMocks的路径在测试环境下可访问,避免404错误 - 生产环境下要移除测试相关的全局变量判断,或者通过构建工具(比如Webpack、Angular CLI)的环境变量来控制,防止测试代码泄露到生产环境
内容的提问来源于stack exchange,提问作者Manya
相关产品推荐
相关产品推荐

