如何使用内部订阅测试 Angular 服务?

问题描述 投票:0回答:1

我有一个 Angular 18 服务,我正在尝试对其进行单元测试。我有一个

setSelected
消费者只需调用的方法,它会在准备好时更新主题

@Injectable({ providedIn: 'root' })
export class TimesheetService {
  private readonly apiService = inject(ApiService);
  
  private readonly selectedTimesheetSubject$ = new BehaviorSubject<ITimesheet| undefined>(undefined);
  private readonly isLoadingSubject$ = new BehaviorSubject(false);
  
  public readonly selectedTimesheet$ = this.selectedTimesheetSubject$.asObservable();
  public readonly isLoading$ = this.isLoadingSubject$.asObservable();
  
  public setSelected(weekEndingDate: string): void {
    this.isLoadingSubject$.next(true);
    this.error = undefined;

    this.apiService.getTimesheet$(weekEndingDate).subscribe({
      next: (res) => {
        this.isLoadingSubject$.next(false);
        this.selectedTimesheetSubject$.next(res);
      },
      error: (err) => {
        this.isLoadingSubject$.next(false);
        console.error('error!', err);
      },
    });
  }

  public clearSelected(): void {
    this.selectedTimesheetSubject$.next(undefined);
  }
}

我如何测试这个?我需要测试当调用该方法时,它会按照我的预期更新所有主题,但我不知道如何挂钩或等待内部的订阅

it('should set the selected timesheet based on a passed in date', async () => {
  const weekEnding = DateTime.fromISO('2024-11-02');
  const expectedInterval = Interval.fromDateTimes(weekEnding, weekEnding);
  injectedApiService.getTimesheet$.and.nextOneTimeWith([mockSelfTimesheet]);

  service.setSelected('2024-11-02');

  //Check that the initial values are correct BEFORE the subscription completes
  expect(await lastValueFrom(service.isLoading$)).toBeTrue();
  expect(await lastValueFrom(service.selectedTimesheet$)).toBeUndefined();
  expect(injectedApiService.getTimesheet$).toHaveBeenCalledOnceWith(expectedInterval, false);

  //How do I check these values AFTER the subscription completes?
  // expect(await lastValueFrom(service.isLoading$)).toBeFalse();
  // expect(await lastValueFrom(service.selectedTimesheet$)).toEqual(mockSelfTimesheet);
});

现在通过上述测试我刚刚得到这个错误

Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)

我不确定我做错了什么。 在订阅完成之前和之后,如何调用服务方法并测试面向外部的 Observables 的值?

angular jasmine angular-test
1个回答
0
投票

您可以使用

fakeAsync
flush
组合使测试用例等待可观察的结果。

对于

BehaviorSubject
,可以直接访问
.value
属性并获取内部值。

要访问对象内部的私有属性,请使用

test['someProperty']
语法。

注释中解释了为什么对测试用例进行注释。

it('should set the selected timesheet based on a passed in date', fakeAsync(() => {
  const mockSelfTimesheet = {};
  // const weekEnding = DateTime.fromISO('2024-11-02');
  // const expectedInterval = Interval.fromDateTimes(weekEnding, weekEnding);
  mockedApiService.getTimesheet$.and.returnValue(of([mockSelfTimesheet]));

  service.setSelected('2024-11-02');

  // you cannot test the below subjects because they are async actions just like the API call, so it will wait for both to complete by that time the subject becomes false
  // expect(service['isLoadingSubject$'].value).toBeTrue();
  // expect(service['selectedTimesheetSubject$'].value).toBeUndefined();
  expect(mockedApiService.getTimesheet$).toHaveBeenCalledOnceWith(
    '2024-11-02'
  );
  flush();
  //How do I check these values AFTER the subscription completes?
  expect(service['isLoadingSubject$'].value).toBeFalse();
  expect(service['selectedTimesheetSubject$'].value).toEqual([
    mockSelfTimesheet,
  ]);
}));

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.