Angular - 如何正确测试在Observable订阅块中执行的操作?

jer*_*elc 0 testing asynchronous jasmine typescript angular

我必须为角度5应用编写单元测试.为了做到这一点,我使用jasmine + jest(我们在公司使用jest而不是karma来测试速度).

为了测试我的组件行为(参见下面的代码),我创建了一个测试,订阅与测试中Observable的组件相同,然后等待2秒,希望组件的订阅代码块有足够的时间来完成,然后,寻找组件的内部变化.

问题是随着测试次数的增加,测试完成所需的时间也会增加.我个人认为必须有一种更好的方法来测试下面这类代码.

  • 你会如何处理这种情况?我试着看async但却无法找到适合我需要的方法.
  • 如何在组件的订阅块之后确保我的测试正在运行?
  • 如何避免等待2秒钟开始测试,而只是等待组件的订阅块完成?

在此先感谢您的帮助.


  • 零件:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { SomeService } from './some.service';

@Component({
  selector: 'app-dummy',
  templateUrl: './dummy.component.html',
  styleUrls: ['./dummy.component.scss']
})
export class DummyComponent implements OnInit, OnDestroy {
  isEditable: Boolean;
  //...
  private aSubscriber;

  constructor(private someService: SomeService) {
    this.aSubscriber = someService.anObservable$.subscribe(value => {
      this.isEditable = value;
    });
  }

  ngOnInit() { }

  ngOnDestroy() {
    this.aSubscriber.unsubscribe();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 服务:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class SomeService {
  private aSubject = new Subject<any>();
  
  anObservable$ = this.aSubject.asObservable();
  
  constructor() { }

  notify(value) {
    this.aSubject.next(value);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 规格文件:

import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';

import { DummyComponent } from './dummy.component';
import { SomeService } from './some.service';

describe('DummyComponent', () => {
  let component: DummyComponent;
  let fixture: ComponentFixture<DummyComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [DummyComponent],
      providers: [SomeService]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(DummyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should subscribe to anObservable and set values according to the received one',
    inject([SomeService], (service: SomeService) => {
      service.anObservable$.subscribe(value => {
        setTimeout(() => { }, 2000);
        //Test that values are correctly set in the component under test.
        expect(component.isEditable).toBeTruthy();
        //...
      });

      service.notify(true);
  }));
});
Run Code Online (Sandbox Code Playgroud)

Gre*_*eek 5

我经常发现我的代码可以在某种程度上进行重构以适应测试,而重构通常会使代码不那么紧密耦合,更灵活,更易于阅读.

在这种情况下,我建议您在订阅的"下一个"处理程序中获取当前的代码,并将其解压缩到单独的方法中.

例如,拿走

  constructor(private someService: SomeService) {
    this.aSubscriber = someService.anObservable$.subscribe(value => {
      this.isEditable = value;
    });
  }
Run Code Online (Sandbox Code Playgroud)

并重构为:

  constructor(private someService: SomeService) {
    this.aSubscriber = someService.anObservable$.subscribe(value => {
      this.onValue(value);
    });
  }

  private onValue(value) {
    this.isEditable = value;
  }
Run Code Online (Sandbox Code Playgroud)

然后,您可以直接测试"onValue"方法,而无需测试Observable.我认为,除非你使用Observable做更高级的事情(将它传递给map(),filter()等),否则你不需要测试Observables,并且你删除了测试的异步方面.

如果你有一个高级管道,你可能应该单独测试它.像这样拆分它可以分别测试两种情况.