AngularJS组件Jasmine测试:如何Mock MutationObserver?
解决Jasmine中Mock AngularJS组件的MutationObserver问题
这个问题我之前处理过类似的场景,出现NotFoundError: DOM Exception 8通常是因为测试环境中MutationObserver尝试监听一个还未完全渲染到DOM中的元素,或者原生MutationObserver的实现在测试环境里有兼容性问题。下面是具体的Mock方案和测试步骤:
1. 在测试前Mock全局的MutationObserver
我们需要用Jasmine的Spy对象替换浏览器原生的MutationObserver,避免它执行真实的DOM监听逻辑。在beforeEach块中添加以下代码:
beforeEach(function() { // 创建一个带有observe和disconnect方法的Spy对象 window.MutationObserver = jasmine.createSpyObj('MutationObserver', ['observe', 'disconnect']); // 如果需要测试回调逻辑,给构造函数添加模拟实现,保存回调函数 window.MutationObserver.and.callFake(function(callback) { // 保存传入的回调,方便后续手动触发 this._mutationCallback = callback; // 返回带有Spy方法的实例对象 return { observe: window.MutationObserver.observe, disconnect: window.MutationObserver.disconnect }; }); });
这段代码的作用是:
- 替换全局的
MutationObserver为Jasmine Spy,这样组件中new MutationObserver()的调用会被拦截 - 保存组件传入的回调函数,方便我们后续手动触发,模拟属性变化的场景
2. 确保组件DOM完全渲染后再执行测试逻辑
很多时候错误是因为组件还没完成渲染,MutationObserver就开始监听元素了。在编译组件后,一定要调用$scope.$digest()触发Angular的脏检查,确保组件的模板渲染完成:
beforeEach(function() { // 配置$templateCache(你的原有代码) $templateCache.put('your-component-template.html', '<div id="panel"></div><div id="viewport"></div>'); // 编译组件 var componentElement = $compile('<your-custom-component></your-custom-component>')($scope); // 触发脏检查,完成DOM渲染 $scope.$digest(); // 可以在这里获取元素,用于后续断言 this.panelElement = componentElement.find('#panel'); this.viewportElement = componentElement.find('#viewport'); });
3. 测试MutationObserver的调用和回调逻辑
如果要验证组件是否正确初始化了MutationObserver,或者测试属性变化时#panel的hide-div类是否切换,可以通过以下方式:
验证Observer是否被正确调用
it('should initialize MutationObserver on #viewport element', function() { // 获取MutationObserver的构造调用记录 const observerCall = window.MutationObserver.calls.first(); // 验证构造函数被调用 expect(window.MutationObserver).toHaveBeenCalled(); // 验证observe方法被调用,且目标元素是#viewport expect(window.MutationObserver.observe).toHaveBeenCalledWith(this.viewportElement[0], jasmine.any(Object)); });
模拟属性变化,测试类切换逻辑
it('should toggle "hide-div" class on #panel when #viewport attribute changes', function() { // 获取组件创建的MutationObserver实例 const observerInstance = window.MutationObserver.calls.mostRecent().returnValue; // 模拟一个属性变化的Mutation记录 const mockMutations = [{ type: 'attributes', attributeName: 'data-visible', // 替换成组件监听的属性名 target: this.viewportElement[0] }]; // 手动触发组件的Mutation回调 observerInstance._mutationCallback(mockMutations); // 断言#panel的类是否正确切换 expect(this.panelElement.hasClass('hide-div')).toBe(true); });
4. 额外排查点
如果还是出现NotFoundError,可以检查:
- 确认模板中确实存在
id="panel"和id="viewport"的元素,可在测试中通过console.log(componentElement.html())查看渲染后的DOM - 确保组件中初始化MutationObserver的代码是在
$onInit(控制器)或link函数中执行的,这些钩子需要$digest触发后才会执行
内容的提问来源于stack exchange,提问作者user4235401
相关产品推荐
相关产品推荐

