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
相关产品推荐
相关产品推荐

