如何正确编写Angular复制剪贴板指令的Jasmine测试用例?
解决Angular CopyClipboard指令测试报错问题
你实现的Angular CopyClipboard指令逻辑没问题,但测试代码存在mock方式错误,导致出现Expected a spy, but got Object({ writeText: <getter> })的报错。以下是错误原因分析和修正后的测试代码:
错误原因
navigator.clipboard的writeText是getter属性,用{ ...global.navigator.clipboard }复制对象时,无法正确复制getter,后续替换成spy的操作不会生效,测试时调用的还是原生方法,Jasmine自然识别不出这是一个spy。另外测试中误用了KeyboardEvent模拟点击事件,应该用MouseEvent。
修正后的完整测试代码
import { Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { configureTestSuite } from 'ng-bullet'; import { CopyClipboardDirective } from './copy-to-clipboard.directive'; describe('Copy to Clipboard directive', () => { let component: Dummy; let fixture: ComponentFixture<Dummy>; let directiveEl: any; let element: any; let writeSpy: jasmine.Spy; // 保存原生的clipboard对象,用于测试后恢复 const originalClipboard = global.navigator.clipboard; configureTestSuite(() => { TestBed.configureTestingModule({ declarations: [Dummy, CopyClipboardDirective], }); }); beforeEach(() => { // 1. 创建writeText的spy,并mock全局navigator.clipboard writeSpy = jasmine.createSpy('writeText'); Object.defineProperty(global.navigator, 'clipboard', { value: { writeText: writeSpy }, writable: true }); fixture = TestBed.createComponent(Dummy); component = fixture.componentInstance; directiveEl = fixture.debugElement.query(By.directive(CopyClipboardDirective)); element = fixture.debugElement.query(By.css('div')); fixture.detectChanges(); }); // 测试结束后恢复原生clipboard,避免影响其他测试 afterEach(() => { Object.defineProperty(global.navigator, 'clipboard', { value: originalClipboard, writable: true }); }); it('should create an instance', () => { expect(directiveEl).toBeTruthy(); }); it('default event should be prevented', () => { // 用MouseEvent模拟点击事件,而非KeyboardEvent const event = new MouseEvent('click', { cancelable: true }); element.nativeElement.dispatchEvent(event); expect(event.defaultPrevented).toBeTruthy(); }); it('copies the right text', () => { const event = new MouseEvent('click', { cancelable: true }); directiveEl.nativeElement.dispatchEvent(event); expect(writeSpy).toHaveBeenCalledWith('copy text'); }); it('does not copy when payload is empty', () => { const event = new MouseEvent('click', { cancelable: true }); // 先清空payload,再触发点击 directiveEl.componentInstance.payload = ''; element.nativeElement.dispatchEvent(event); expect(writeSpy).not.toHaveBeenCalled(); }); }); @Component({ template: `<div copyClipboard="copy text"></div>` }) class Dummy {}
关键修改点
- 正确mock全局clipboard:通过
Object.defineProperty直接替换global.navigator.clipboard,确保指令调用的是spy方法 - 测试前后环境恢复:在
afterEach中恢复原生clipboard,避免污染其他测试用例 - 修正事件类型:点击事件使用
MouseEvent,符合真实场景 - 修复空payload测试逻辑:先将payload设为空,再触发点击,验证不执行复制操作
内容的提问来源于stack exchange,提问作者Aayushi
相关产品推荐
相关产品推荐

