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

无法测试MutationObserver observe()回调的问题排查

问题原因分析

你在测试中全局替换了MutationObserver为一个空实现的mock,但这个mock的observe方法只是一个空的jest.fn(),没有任何监听DOM变化并触发传入回调的逻辑。所以即使你修改了DOM元素的属性,也不会触发你定义的回调函数。

解决方案

下面提供两种可行的测试方案,根据你的测试需求选择:

方案1:完善MutationObserver的Mock实现(单元测试场景)

手动让mock的observe方法记录回调函数,在DOM变化时主动触发回调:

describe('MutationObserverDemoComponent', () => {
  let component: MutationObserverDemoComponent;
  let fixture: ComponentFixture<MutationObserverDemoComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [MutationObserverDemoComponent]
    });

    fixture = TestBed.createComponent(MutationObserverDemoComponent);
    component = fixture.componentInstance;
    
    // 完善MutationObserver的mock,记录回调和目标元素
    const mutationObserverMock = jest.fn<MutationObserver, [MutationCallback]>((callback) => {
      const observer = {
        observe: jest.fn((target, options) => {
          observer.target = target;
          observer.options = options;
          observer.callback = callback;
        }),
        disconnect: jest.fn(),
        takeRecords: jest.fn(),
        target: null,
        options: null,
        callback: null
      };
      return observer;
    });
    global.MutationObserver = mutationObserverMock;
  });

  it('should observe DOM changes', () => {
    const target = fixture.debugElement.query(By.css('.mutation-div')).nativeElement;

    let observedMutations: MutationRecord[] = [];
    const observer = new MutationObserver((mutations: MutationRecord[]) => {
      observedMutations = observedMutations.concat(mutations);
    });

    observer.observe(target, { attributes: true, childList: true, characterData: true });

    target.setAttribute('data-mutation', 'testing');

    // 手动触发mock的回调,传入模拟的MutationRecord
    const mockMutation: MutationRecord = {
      type: 'attributes',
      target,
      attributeName: 'data-mutation',
      oldValue: null,
      addedNodes: [],
      removedNodes: [],
      previousSibling: null,
      nextSibling: null,
      attributeNamespace: null,
      characterData: null,
      oldCharacterData: null
    };
    observer.callback([mockMutation]);

    expect(observedMutations.length).toBe(1);
    expect(observedMutations[0].attributeName).toBe('data-mutation');
  });
});

方案2:使用原生MutationObserver(集成测试场景)

Jest的jsdom环境支持原生MutationObserver,但要注意其回调是异步执行的,需要等待微任务队列完成:

describe('MutationObserverDemoComponent', () => {
  let component: MutationObserverDemoComponent;
  let fixture: ComponentFixture<MutationObserverDemoComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [MutationObserverDemoComponent]
    });

    fixture = TestBed.createComponent(MutationObserverDemoComponent);
    component = fixture.componentInstance;
    // 移除mock,使用原生MutationObserver
  });

  it('should observe DOM changes', async () => {
    const target = fixture.debugElement.query(By.css('.mutation-div')).nativeElement;

    let observedMutations: MutationRecord[] = [];
    const observer = new MutationObserver((mutations: MutationRecord[]) => {
      observedMutations = observedMutations.concat(mutations);
    });

    observer.observe(target, { attributes: true, childList: true, characterData: true });

    target.setAttribute('data-mutation', 'testing');
    
    // 等待微任务执行,确保MutationObserver回调触发
    await fixture.whenStable();

    expect(observedMutations.length).toBeGreaterThan(0);
    expect(observedMutations[0].attributeName).toBe('data-mutation');
  });
});

额外提示:测试组件内部的MutationObserver逻辑

如果你的组件内部初始化了MutationObserver,测试时应该聚焦于组件的行为而非手动创建Observer:

it('should initialize MutationObserver on init', () => {
  fixture.detectChanges(); // 触发ngOnInit

  // 验证MutationObserver是否被实例化
  expect(global.MutationObserver).toHaveBeenCalled();
  const observerInstance = global.MutationObserver.mock.results[0].value;
  expect(observerInstance.observe).toHaveBeenCalled();
});

it('should handle mutations correctly', () => {
  fixture.detectChanges();
  // 获取组件内部传入的回调函数
  const observerCallback = global.MutationObserver.mock.calls[0][0];
  const mockMutations: MutationRecord[] = [{ type: 'attributes' } as MutationRecord];

  // 触发回调,验证组件的处理逻辑
  observerCallback(mockMutations);
  // 例如:expect(component.someProperty).toBe('expected-value');
});

内容的提问来源于stack exchange,提问作者Ricky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 04:13:27