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

Angular Jest单元测试mock HTMLElement报自定义元素注册表错误

报错根因

两个报错本质都是Jest默认依赖的JSDOM环境的固有规则限制:

  • HTMLImageElement、HTMLElement、Element这类Web IDL定义的DOM抽象接口,不允许直接通过new关键字手动实例化。浏览器和JSDOM都只会在解析DOM、内部生成节点的时候调用这些构造器,用户手动调用就会抛出Illegal constructor或者你看到的"构造器不在自定义元素注册表内"的错误。
  • 就算你通过类型断言绕开TS检查,手动构造的对象也不满足你指令里的判断逻辑:你的onClick方法是拿target.constructor和HTMLImageElement做全等匹配,手动拼的对象、或者通过其他方式生成的对象原型链不对,constructor匹配不上,逻辑根本走不到图片处理分支。
  • 另外你原测试代码直接传null作为BsModalService的依赖也有问题,后续调用openLightbox的时候会触发空指针错误,同时你没有给openLightbox方法加监视桩,toHaveBeenCalled断言也无法正常执行。
正确测试实现

创建DOM元素的标准方式是调用document.createElementAPI,这个方法在JSDOM中完全支持,生成的元素原型链和真实浏览器完全一致,constructor会正确对应到对应的DOM构造器,能完美匹配你指令里的判断逻辑。

完整可运行的测试代码如下:

import { HTMLClickDirective } from "./html-click.directive";
import { BsModalService } from "ngx-bootstrap/modal";

describe('HTMLClickDirective', () => {
  let mockModalService: jest.Mocked<BsModalService>;
  let directive: HTMLClickDirective;

  beforeEach(() => {
    // 初始化mock的modal服务
    mockModalService = {
      show: jest.fn().mockReturnValue({ hide: jest.fn() })
    } as unknown as jest.Mocked<BsModalService>;
    directive = new HTMLClickDirective(mockModalService);
  });

  it('点击图片节点时应该触发灯箱打开逻辑', () => {
    // 正确创建img节点,不要用new HTMLImageElement
    const testImg = document.createElement('img');
    testImg.setAttribute('src', 'test-image-path.png');
    // 给openLightbox方法加监视桩
    const openLightboxSpy = jest.spyOn(directive, 'openLightbox');

    // 构造mock点击事件,补全指令里会调用的preventDefault方法
    const mockClick = {
      target: testImg,
      preventDefault: jest.fn()
    } as unknown as MouseEvent;

    directive.onClick(mockClick);

    // 断言逻辑符合预期
    expect(mockClick.preventDefault).toHaveBeenCalled();
    expect(openLightboxSpy).toHaveBeenCalledWith('test-image-path.png');
    expect(mockModalService.show).toHaveBeenCalled();
  });

  it('点击STRONG节点时应该触发对应锚点滚动', () => {
    const testStrong = document.createElement('strong');
    testStrong.innerText = 'intro';
    // 提前插入对应id的锚点节点
    const anchorEl = document.createElement('div');
    anchorEl.id = 'intro';
    anchorEl.scrollIntoView = jest.fn();
    document.body.append(anchorEl);

    const mockClick = {
      target: testStrong,
      preventDefault: jest.fn()
    } as unknown as MouseEvent;

    directive.onClick(mockClick);
    expect(mockClick.preventDefault).toHaveBeenCalled();
    expect(anchorEl.scrollIntoView).toHaveBeenCalled();
  });
});

注意:不要依赖TypeScript类型断言伪造DOM节点,你的指令逻辑是基于运行时的原型链constructor做判断,只有通过document.createElement生成的真实DOM节点才能满足判断条件,字面量对象、手动new构造器生成的对象即使TS类型校验通过,运行时逻辑也会不符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 11:15:30