简体   繁体   English

对服务中的 BehaviorSubject 进行单元测试

[英]Unit testing a BehaviorSubject in a service

I'm working on a toy project to learn unit testing in Angular (replaced Karma with Jest).我正在做一个玩具项目来学习 Angular 中的单元测试(用 Jest 替换 Karma)。 I've seen similar questions but all of them handle subscriptions differently, so I wasn't able to get the exact answer to work for me.我见过类似的问题,但他们都以不同的方式处理订阅,所以我无法得到适合我的确切答案。

TL;DR: I'm trying to test a BehaviorSubject, but rather than the value I'm expecting, it's sending it's initial value to me. TL;DR:我正在尝试测试一个 BehaviorSubject,但不是我期望的值,而是将它的初始值发送给我。 I understand the ordering of the subscription in the test matters, but I couldn't figure the proper ordering yet.我了解测试中订阅的顺序,但我还无法确定正确的顺序。

I have a BehaviorSubject in a service:我在服务中有一个 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);
  }

  // ...
}

and a listener on a component, which subscribes to the Observable using the async pipe in the template和一个组件上的监听器,它使用模板中的异步 pipe 订阅 Observable

export class SomeComponent implements OnInit, OnDestroy {
  // ...
  remainingSeconds$ = this.timer.seconds$.pipe(tap(seconds => console.log(`remainingSeconds$: ${seconds}`)));

  constructor(public timer: TimerService) {}
}

Subscription happens in the template as follows订阅在模板中发生如下

<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>

I would like to test that when the setTimer function in the service is called, the remainingSeconds$ observable in the component also gets the correct value.我想测试一下,当调用服务中的setTimer function 时,组件中的remainingSeconds$ observable 也能得到正确的值。 Especially, if the "remainingSeconds" value in the service is undefined, I'd like to make sure that the remainingSeconds$ observable gets the updated undefined value, and thus the appropriate UI element is hidden.特别是,如果服务中的“remainingSeconds”值未定义,我想确保剩余的Seconds$ observable 获得更新的未定义值,从而隐藏适当的UI 元素。

The problem I'm having with the test (for which the code is below) is that when I call service.setTimer(undefined) from the component, I'm getting the initial value of the BehaviorSubject rather than the undefined value I wanted (which makes the test useless).我在测试中遇到的问题(代码如下)是,当我从组件调用service.setTimer(undefined)时,我得到的是 BehaviorSubject 的初始值,而不是我想要的undefined值(这使测试无用)。

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);
}));

I'm adding a stackblitz link , but sadly I couldn't run npm test on there.我正在添加一个 stackblitz 链接,但遗憾的是我无法在那里运行 npm 测试。 Hopefully it'll give some basic idea as to what I'm trying to accomplish.希望它能给我一些关于我想要完成的事情的基本想法。

You could quickly solve this with:您可以通过以下方式快速解决此问题:

component.remainingSeconds$.pipe(skip(1)).subscribe(...)

Try尝试

TestBed.configureTestingModule({
        declarations: [
          AppComponent
        ],
        providers: [
          TimerService
        ]
      }).compileComponents();

and remove TestBed.inject并删除 TestBed.inject

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM