You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:17:00