【发布时间】:2020-09-09 06:58:29
【问题描述】:
我正在做一个玩具项目来学习 Angular 中的单元测试(用 Jest 代替 Karma)。我见过类似的问题,但他们都以不同的方式处理订阅,所以我无法得到适合我的确切答案。
TL;DR:我正在尝试测试一个 BehaviorSubject,但它发送的不是我期望的值,而是它的初始值。我了解测试事项中订阅的顺序,但我还想不出正确的顺序。
我在服务中有一个 BehaviorSubject:
export class SomeService {
// ...
remainingSeconds: number;
private _seconds: Subject<number> = new BehaviorSubject(RegularTimerSeconds.WORK_TIME);
public readonly seconds$: Observable<number> = this._seconds.asObservable();
setTimer(seconds: number) {
this.remainingSeconds = seconds;
this._seconds.next(this.remainingSeconds);
}
// ...
}
和一个组件上的监听器,它使用模板中的异步管道订阅 Observable
export class SomeComponent implements OnInit, OnDestroy {
// ...
remainingSeconds$ = this.timer.seconds$.pipe(tap(seconds => console.log(`remainingSeconds$: ${seconds}`)));
constructor(public timer: TimerService) {}
}
订阅在模板中发生如下
<ng-container *ngIf="{
remainingSeconds: remainingSeconds$ | async,
currentState: currentState$ | async,
informationText: informationText$ | async,
doneCounter: doneCounter$ | async
} as data">
<!-- ... -->
<div class="regular-timer-container" *ngIf="data.remainingSeconds !== undefined">
<p>Remaining seconds: {{ data.remainingSeconds }}</p>
</div>
</ng-container>
我想测试一下,当服务中的setTimer函数被调用时,组件中的remainingSeconds$ observable也得到了正确的值。特别是,如果服务中的“remainingSeconds”值未定义,我想确保剩余的Seconds$ observable 获得更新的未定义值,从而隐藏适当的UI 元素。
我在测试中遇到的问题(代码如下)是,当我从组件调用 service.setTimer(undefined) 时,我得到的是 BehaviorSubject 的初始值而不是 undefined 值 I想要(这使得测试毫无用处)。
it(`shouldn't render the timer component if the seconds value is unset`, async(() => {
component.remainingSeconds$
.subscribe(
seconds => {
expect(seconds).toEqual(undefined);
fixture.detectChanges();
const timerContainerDom: DebugElement = fixture.debugElement;
const regularTimerComponent = timerContainerDom.query(By.directive(AppComponent));
expect(regularTimerComponent).toBeFalsy();
}
);
timerService.setTimer(undefined);
}));
我正在添加a stackblitz link,但遗憾的是我无法在那里运行 npm test。希望它能给我一些关于我要完成的工作的基本想法。
【问题讨论】:
-
你试过
component.remainingSeconds$.pipe(skip(1)).subscribe(...)吗? -
@AndreiGătej 谢谢你的回复——我没用过。从理论上和实际上它都可以正常工作,因为它跳过了 BehaviorSubject 的初始值,这解决了我的问题。同时,我想知道测试 BehaviorSubjects 是否有关于它们的一般“最佳实践”。如果您可以将此作为答案发布,我会选择它作为好答案。 :)
-
我不知道“一刀切”的解决方案,但是在您的情况下,我要做的是分开任务,问自己:我要测试什么?例如,如果您想测试 observable 在某些事件之后发出正确的值,您可能需要为此使用弹珠测试。如果您想测试某些元素是否正确出现在 DOM 中,我不会考虑事件的真实来源(例如
Subject),而是一个模拟的 observable,它只会发出一些值,让我专注于视图测试。 -
我已经添加了答案。谢谢!
标签: angular unit-testing rxjs