【问题标题】:Unit test valueChanges observable pipeline单元测试值更改可观察管道
【发布时间】:2021-04-11 21:55:10
【问题描述】:

场景

  • LoginPageComponent 期待用户输入。此输入是 6 个字符的键(短语)。
  • 用户输入 8 个字符后,加载状态将设置为忙。加载完成后,状态将是成功或失败。
  • 在失败状态下,会出现一条错误消息。
  • 当密钥有效时,用户将被引导至仪表板。

我要测试什么?

  • 加载时加载状态忙
  • 加载失败时出现错误
  • AuthService 只能使用 6 个字符长的键调用

我的问题是什么?

时间。
如何模拟满足我需求的输入(知道 debounceTime)?另外AuthService 需要一些异步时间来检查密钥,所以我不能直接断言。我也不能订阅可观察链,因为它不是公开的。

代码

export class LoginPage implements OnInit {
  loadingState = LoaderState.None;
  message: string;
  form: FormGroup = this.formBuilder.group({ key: '' });

  constructor(
    private authService: AuthService,
    private formBuilder: FormBuilder
  ) {}

  ngOnInit(): void {
    this.form.get('key')?.valueChanges.pipe(
        tap(() => (this.loadingState = LoaderState.None)),
        debounceTime(350),
        filter((key: string) => key.length === 8),
        tap(() => (this.loadingState = LoaderState.Loading)),
        switchMap((key: string) =>
          of(key).pipe(
            switchMap(() => this.authService.authenticate(key)),
            catchError((error) => this.handleErrorStatusCode(error))
          )
        ),
        tap(() => (this.loadingState = LoaderState.Done))
      )
      .subscribe((_) => {
        console.log('success'); //TODO: Navigate
      });
  }

  private handleErrorStatusCode(error: any): Observable<never> {
    this.loadingState = LoaderState.Failed;
    // Set error logic...
    return EMPTY;
  }
}

【问题讨论】:

    标签: angular unit-testing rxjs jasmine angular-reactive-forms


    【解决方案1】:

    我终于明白了。我一直在思考新的TestScheduler 和大理石测试。但这不是要走的路。相反,来自 Zone.js 的 fakeAsync 非常适合:

    describe('LoginPage', () => {
      let component: LoginPage;
      let mockAuthService: any;
      let fixture: ComponentFixture<LoginPage>;
    
      beforeEach(async () => {
        mockAuthService = jasmine.createSpyObj(['authenticate']);
        await TestBed.configureTestingModule({
          declarations: [LoginPage],
          imports: [ReactiveFormsModule],
          providers: [{ provide: AuthService, useValue: mockAuthService }]
        }).compileComponents();
      });
    
      beforeEach(() => {
        fixture = TestBed.createComponent(LoginPage);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('should create', () => {
        expect(component).toBeTruthy();
      });
    
      it('is in busy state while loading', fakeAsync(() => {
        mockAuthService.authenticate.and.returnValue(of('result').pipe(delay(100)));
    
        component.form.patchValue({ token: '123456' });
        tick(250);
        discardPeriodicTasks();
    
        expect(component.loadingState).toBe(LoaderState.Loading);
      }));
    
      it('it is in error state when auth service denies', fakeAsync(() => {
        mockAuthService.authenticate.and.returnValue(throwError({ status: 401 }));
    
        component.form.patchValue({ token: '123456' });
        tick(250);
        expect(component.loadingState).toBe(LoaderState.Failed);
        expect(component.message).toBeDefined();
      }));
    
      it('is in success state when auth service accept the key', fakeAsync(() => {
        mockAuthService.authenticate.and.returnValue(of('result'));
    
        component.form.patchValue({ key: '123456' });
        tick(250);
        expect(component.loadingState).toBe(LoaderState.Done);
      }));
    
      it('resets state on input', fakeAsync(() => {
        mockAuthService.authenticate.and.returnValue(of('token'));
    
        component.form.patchValue({ key: '123456' });
        tick(250);
        expect(component.loadingState).toBe(LoaderState.Done);
    
        component.form.patchValue({ key: '12345' });
        tick(250);
        expect(component.loadingState).toBe(LoaderState.Idle);
      }));
    
      it('should not have error message after construction', () => {
        expect(component.message).toBeNull();
      });
    
      it('is in idle state after construction', () => {
        expect(component.loadingState).toBe(LoaderState.Idle);
      });
    });
    

    使用tick() 方法时间操作没问题!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-08
      • 1970-01-01
      • 1970-01-01
      • 2019-09-22
      • 2021-12-16
      • 1970-01-01
      相关资源
      最近更新 更多