在没有detectChanges的情况下测试observable会失败

Oro*_*dan 10 unit-testing asynchronous observable jasmine angular

我正在复制一些Angular文档的例子,以提高我对角度单元测试的理解,最后我在一个简单的测试用例中,当我无法弄清楚发生了什么.

这是我的app.component.ts文件,当我有一个方法"getQuote"从服务获得报价.

@Component({...})
export class AppComponent {
  errMsg: string;
  quote: Observable<string>;

  constructor (private twainService: TwainService) {}

  getQuote () {
    this.quote = this.twainService.getQuote().pipe(
      catchError(err => {
        this.errMsg = err;
        return of('...');
      })
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,我创建了一个测试来验证我的errMsg道具是否正确更新,以防我从twainService.getQuote方法收到错误:

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

  let getQuoteSpy;

  beforeEach(async(() => {
    const twainService = jasmine.createSpyObj('TwainService', ['getQuote']);
    getQuoteSpy = twainService.getQuote.and.returnValue(of(testQuote));

    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      providers: [
        { provide: TwainService, useValue: twainService }
      ]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.debugElement.componentInstance;
  });

  it('should get error msg when TwainService fails', async(async () => {
    const errMsg = 'TwainService fails';
    getQuoteSpy.and.returnValue(throwError(errMsg));

    component.getQuote();
    await fixture.whenStable();

    expect(component.errMsg).toBe(errMsg);
  }));
});
Run Code Online (Sandbox Code Playgroud)

但问题是:这个测试总是失败,我无法看到什么是错的.

到处玩,我设法发现添加一个像"fixture.detectChanges()"这样的测试工作,但我不明白为什么.我认为detectChanges方法仅用于将更改传播到组件视图.

it('should get error msg when TwainService fails', async(async () => {
    const errMsg = 'TwainService fails';
    getQuoteSpy.and.returnValue(throwError(errMsg));

    component.getQuote();
    fixture.detectChanges();
    await fixture.whenStable();

    expect(component.errMsg).toBe(errMsg);
  }));
Run Code Online (Sandbox Code Playgroud)

我测试了async,fakeAsync,并使用同步observable直接发送错误和异步observable,结果总是相同的.

如果有人能帮我理解那里发生了什么:)

sho*_*ukh 9

我猜你只需要在测试中quote调用getQuote方法后订阅你的Observable :

 it('should get error msg when TwainService fails', async(async () => {
    const errMsg = 'TwainService fails';
    getQuoteSpy.and.returnValue(throwError(errMsg));

    component.getQuote();
    component.quote.subscribe();
    await fixture.whenStable();

    expect(component.errMsg).toBe(errMsg);
  }));
Run Code Online (Sandbox Code Playgroud)

因此,当您调用component.getQuote()测试时 - 它只是将this.quote属性设置为'冷'可观察,并且为了看到它catchError被触发,您必须订阅Observable.这将运行它,最终你会根据你的模拟数据得到一个错误throwError(errMsg).

编辑

根据你问题的第二部分:

到处玩,我设法发现添加一个像"fixture.detectChanges()"这样的测试工作,但我不明白为什么.

我也想通了,很可能你async在组件的模板中的某个地方使用了管道:{{ quote | async }}.在引擎盖下,angular的异步管道订阅了一个quoteObservable并返回它发出的最新值.这就是我们需要调用detectChanges方法的原因,之后 - 异步管道将开始订阅quoteObservable(并且测试将按预期工作).在这种情况下,我们不需要手动订阅quoteObservable(异步管道处理它).是的 - 你是对的:我认为detectChanges方法仅用于将更改传播到组件视图.

您可以在stackblitz示例中测试这两个示例.希望这会有所帮助:)

  • 谢谢,我确实忘了你需要订阅你的observable ^^'就像我现在预期的那样 (2认同)