如何测试向Angular DOCUMENT注入脚本的LoadService服务功能
问题根源
Jest搭配的JSDOM环境默认禁止加载外部资源,你注入的script标签不会实际发起网络请求,因此所有加载相关事件都不会触发,导致Promise一直处于pending状态最终超时。
单元测试场景下不建议依赖外部网络请求,我们只需要验证服务的行为逻辑即可,不需要真实加载远程脚本。
正确测试方案
我们通过手动触发script元素的加载事件来验证服务逻辑,不需要发起真实网络请求,修改后的测试代码如下:
/** * @jest-environment jsdom */ import { test, expect, beforeAll } from '@jest/globals'; import { TestBed } from '@angular/core/testing'; import { LoadService } from './load-scripts.service'; import { DOCUMENT } from '@angular/common'; let service: LoadService; let doc: Document; beforeAll(() => { TestBed.configureTestingModule({ providers: [LoadService], // 修正原测试中provider写错的问题 }); service = TestBed.inject(LoadService); doc = TestBed.inject(DOCUMENT); // 拿到测试环境的document实例 }); test('service should be created', () => { expect(service).toBeTruthy(); }); test('the script should be loaded successfully', async () => { // 调用load方法 const loadPromise = service.load('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js'); // 找到刚插入到head的script元素 const insertedScript = doc.head.querySelector('script[src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"]') as HTMLScriptElement; expect(insertedScript).toBeTruthy(); expect(insertedScript.async).toBe(true); // 验证async属性正确设置 // 手动触发load事件 insertedScript.dispatchEvent(new Event('load')); // 验证Promise正常resolve await expect(loadPromise).resolves.not.toThrow(); }); test('the script load should throw error when failed', async () => { const loadPromise = service.load('invalid-url'); const insertedScript = doc.head.querySelector('script[src="invalid-url"]') as HTMLScriptElement; // 手动触发error事件 insertedScript.dispatchEvent(new Event('error')); // 验证Promise正常reject await expect(loadPromise).rejects.toBeDefined(); });
可选方案(不推荐,仅作参考)
如果你确实需要真实加载远程脚本,可以开启JSDOM的资源加载能力,只需要在测试文件顶部修改JSDOM配置:
/** * @jest-environment jsdom * @jest-environment-options {"resources": "usable"} */
该方案的缺点:
- 依赖网络环境,网络波动会导致测试失败
- 测试执行速度慢
- 外部资源不可用时测试直接失败,不符合单元测试稳定性要求
服务优化建议
你当前的服务里监听了loaded、complete、readystatechange三类冗余事件,这些都是旧IE版本的兼容用事件,现代浏览器(IE11及以上)只需监听load和error两类事件就足够覆盖所有场景。
内容的提问来源于stack exchange,提问作者Sherif eldeeb
相关产品推荐
相关产品推荐

