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

Angular中如何创建HtmlElement实例测试滚动判断方法?

Angular滚动检测方法的测试:Mock事件问题

待测试的方法

public checkIfScrolledToBottomOfPanel(event: Event) {
    console.log('event.target instanceof HTMLElement ', event.target instanceof HTMLElement);
    return (
      event.target instanceof HTMLElement &&
      event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight
    );
}

遇到的问题

测试时无法正确Mock事件对象,核心问题是event.target instanceof HTMLElement始终返回false,尝试过以下几种方式均无效:

尝试1:直接Mock普通对象

it('should return true if scrolled to the bottom of the panel', () => {
  const mockEvent = {
    target: {
      offsetHeight: 500,
      scrollTop: 300,
      scrollHeight: 100
    }
  } as Event;

  const result = component.checkIfScrolledToBottomOfPanel(mockEvent);
  expect(result).toBe(true);
});

尝试2:使用jasmine.createSpyObj

const mockEvent = jasmine.createSpyObj('Event', ['target']);
mockEvent.target = jasmine.createSpyObj('HTMLElement', ['offsetHeight', 'scrollTop', 'scrollHeight']);

mockEvent.target.offsetHeight.and.returnValue(100);
mockEvent.target.scrollTop.and.returnValue(100);
mockEvent.target.scrollHeight.and.returnValue(200);
// 无效

尝试3:使用真实DOM元素

尝试用document.createElement('div')创建target,但不知道如何设置offsetHeight、scrollTop、scrollHeight这些只读属性。

解决方案

方案1:通过原型链模拟HTMLElement实例

用Object.create(HTMLElement.prototype)创建符合原型链的对象,再用Object.defineProperty设置只读属性:

it('should return true when scrolled to bottom', () => {
  // 创建继承HTMLElement原型的模拟元素
  const mockElement = Object.create(HTMLElement.prototype);
  // 定义只读属性的值
  Object.defineProperty(mockElement, 'offsetHeight', { value: 500 });
  Object.defineProperty(mockElement, 'scrollTop', { value: 300 });
  Object.defineProperty(mockElement, 'scrollHeight', { value: 800 }); // 500+300=800,满足条件

  const mockEvent = { target: mockElement } as Event;
  const result = component.checkIfScrolledToBottomOfPanel(mockEvent);
  
  expect(result).toBe(true);
});

it('should return false when not scrolled to bottom', () => {
  const mockElement = Object.create(HTMLElement.prototype);
  Object.defineProperty(mockElement, 'offsetHeight', { value: 500 });
  Object.defineProperty(mockElement, 'scrollTop', { value: 100 });
  Object.defineProperty(mockElement, 'scrollHeight', { value: 800 });

  const mockEvent = { target: mockElement } as Event;
  const result = component.checkIfScrolledToBottomOfPanel(mockEvent);
  
  expect(result).toBe(false);
});

方案2:使用Jasmine的spyOnProperty拦截真实元素属性

创建真实DOM元素,通过spyOnProperty直接mock只读属性的返回值:

it('should return true when scrolled to bottom', () => {
  const div = document.createElement('div');
  
  // 拦截只读属性,返回指定值
  spyOnProperty(div, 'offsetHeight').and.returnValue(500);
  spyOnProperty(div, 'scrollTop').and.returnValue(300);
  spyOnProperty(div, 'scrollHeight').and.returnValue(800);

  const mockEvent = { target: div } as Event;
  const result = component.checkIfScrolledToBottomOfPanel(mockEvent);
  
  expect(result).toBe(true);
});

原因说明

之前的Mock方式失败,是因为创建的target只是普通JS对象,不属于HTMLElement的原型链,导致instanceof HTMLElement判断不通过。上述两种方案要么让对象继承HTMLElement原型,要么用真实元素+属性拦截,既能通过类型判断,又能自定义属性值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:18:34