在Jasmine中测试Angular 4的setTimer Observable函数
首先先梳理下你的setTimer函数核心逻辑,这样我们能针对性覆盖所有代码分支:
setTimer(timeCounter?: number, initialDelay?: number): Observable
{
return Observable.timer((isNumber(initialDelay)? initialDelay * 1000 : DEFAULT_INITIAL_DELAY), DEFAULT_TIME_PERIOD)
.map(i => isNumber(timeCounter)? timeCounter - i : DEFAULT_TIME_COUNTER - i)
.take(isNumber(timeCounter)? timeCounter + 1 : DEFAULT_TIME_COUNTER + 1);
}
在Angular 4中测试RxJS Observable,推荐用fakeAsync/tick工具模拟时间流逝(不用真的等待实际时间),也能精准控制Observable的发射时机。下面一步步来写完整的测试用例。
测试准备
首先在测试文件顶部定义和业务代码一致的常量(如果这些常量是全局或服务内的,直接复用即可):
// 假设你的默认值如下,根据实际业务调整 const DEFAULT_INITIAL_DELAY = 1000; const DEFAULT_TIME_PERIOD = 1000; const DEFAULT_TIME_COUNTER = 5;
如果setTimer是某个服务的方法,先在beforeEach中注入服务:
let timerService: YourTimerService; beforeEach(() => { TestBed.configureTestingModule({ providers: [YourTimerService] }); timerService = TestBed.inject(YourTimerService); });
具体测试用例
1. 不传任何参数时,返回默认倒计时序列
验证初始延迟、倒计时起始值、发射次数都符合默认配置:
it('should emit default countdown sequence when no parameters are passed', fakeAsync(() => { const emittedValues: number[] = []; const subscription = timerService.setTimer().subscribe(val => emittedValues.push(val)); // 先等待默认初始延迟 tick(DEFAULT_INITIAL_DELAY); // 等待每个周期的发射,总共需要DEFAULT_TIME_COUNTER次tick(从5到0共6个值) for (let i = 0; i < DEFAULT_TIME_COUNTER; i++) { tick(DEFAULT_TIME_PERIOD); } expect(emittedValues).toEqual([5, 4, 3, 2, 1, 0]); expect(emittedValues.length).toBe(DEFAULT_TIME_COUNTER + 1); subscription.unsubscribe(); }));
2. 传入timeCounter时,返回对应长度的倒计时
比如传入3,应该发射3,2,1,0四个值:
it('should emit countdown sequence matching the provided timeCounter', fakeAsync(() => { const testCounter = 3; const emittedValues: number[] = []; const subscription = timerService.setTimer(testCounter).subscribe(val => emittedValues.push(val)); tick(DEFAULT_INITIAL_DELAY); for (let i = 0; i < testCounter; i++) { tick(DEFAULT_TIME_PERIOD); } expect(emittedValues).toEqual([3, 2, 1, 0]); expect(emittedValues.length).toBe(testCounter + 1); subscription.unsubscribe(); }));
3. 传入initialDelay时,等待指定延迟后开始发射
比如传入2作为initialDelay,要验证必须等2秒后才会发射第一个值:
it('should start emitting only after the provided initialDelay', fakeAsync(() => { const testDelay = 2; const emittedValues: number[] = []; const subscription = timerService.setTimer(undefined, testDelay).subscribe(val => emittedValues.push(val)); // 等待1999ms,此时还没到初始延迟,应该没有值 tick(1999); expect(emittedValues).toEqual([]); // 再tick1ms,触发第一个值发射 tick(1); expect(emittedValues).toEqual([DEFAULT_TIME_COUNTER]); // 继续等待一个周期,验证第二个值 tick(DEFAULT_TIME_PERIOD); expect(emittedValues).toEqual([DEFAULT_TIME_COUNTER, DEFAULT_TIME_COUNTER - 1]); subscription.unsubscribe(); }));
4. 同时传入timeCounter和initialDelay时,组合逻辑正确
比如传入timeCounter=2、initialDelay=1,验证延迟和倒计时序列都符合预期:
it('should respect both provided timeCounter and initialDelay', fakeAsync(() => { const testCounter = 2; const testDelay = 1; const emittedValues: number[] = []; const subscription = timerService.setTimer(testCounter, testDelay).subscribe(val => emittedValues.push(val)); // 等待指定初始延迟(1*1000ms) tick(testDelay * 1000); expect(emittedValues).toEqual([2]); // 等待第一个周期 tick(DEFAULT_TIME_PERIOD); expect(emittedValues).toEqual([2, 1]); // 等待第二个周期 tick(DEFAULT_TIME_PERIOD); expect(emittedValues).toEqual([2, 1, 0]); // 再tick不会有新值,因为take已经取了3个值 tick(DEFAULT_TIME_PERIOD); expect(emittedValues.length).toBe(testCounter + 1); subscription.unsubscribe(); }));
5. 传入非数字参数时,自动 fallback到默认值
验证当传入字符串、undefined等非数字参数时,函数会使用默认配置:
it('should fallback to default values when non-number parameters are passed', fakeAsync(() => { const emittedValues: number[] = []; // 传入非数字的参数模拟异常场景 const subscription = timerService.setTimer('invalid' as any, null as any).subscribe(val => emittedValues.push(val)); tick(DEFAULT_INITIAL_DELAY); for (let i = 0; i < DEFAULT_TIME_COUNTER; i++) { tick(DEFAULT_TIME_PERIOD); } expect(emittedValues).toEqual([5, 4, 3, 2, 1, 0]); subscription.unsubscribe(); }));
关键注意事项
- 每个测试用例结束后记得取消订阅,避免内存泄漏
fakeAsync和tick是Angular测试工具的核心,能精准模拟时间推进,适合测试定时器类Observable- 如果你的
isNumber是自定义函数,建议单独测试它的边界情况(比如判断NaN、undefined、null的逻辑)
内容的提问来源于stack exchange,提问作者Quang

