【问题标题】:Testing observables fail without detectChanges没有 detectChanges 测试 observables 失败
【发布时间】:2018-08-22 22:34:50
【问题描述】:

我正在复制 Angular 文档的一些示例以提高我对 Angular 单元测试的理解,当我无法弄清楚发生了什么时,我最终得到了一个简单的测试用例。

当我有一个从服务获取报价的单一方法“getQuote”时,这是我的 app.component.ts 文件。

@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('...');
      })
    );
  }
}

然后,我创建了一个测试来验证我的 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);
  }));
});

但问题是:这个测试总是失败,我看不出有什么问题。

四处玩耍,我设法发现添加如下所示的“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);
  }));

我用 async、fakeAsync 和直接发出错误的同步 observable 和异步 observable 进行了测试,结果总是一样的。

如果有人可以帮助我了解那里发生了什么:)

【问题讨论】:

    标签: angular unit-testing asynchronous jasmine observable


    【解决方案1】:

    我猜你只需要在测试中调用getQuote 方法后订阅你的quote 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);
      }));
    

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

    编辑

    根据你问题的第二部分:

    玩玩后,我发现添加如下所示的“fixture.detectChanges()”可以使测试正常工作,但我不明白为什么。

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

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

    【讨论】:

    • 谢谢你,我确实忘记了你需要订阅你的 observable ^^' 作品,就像我现在预期的那样
    • 欢迎您。这个问题真的很有趣。我还更新了答案并为您问题的第二部分添加了解释-“我设法发现添加如下所示的“fixture.detectChanges()”可以使测试有效,但我不明白为什么。 "
    猜你喜欢
    • 1970-01-01
    • 2015-05-22
    • 2017-10-31
    • 1970-01-01
    • 2021-01-06
    • 2020-02-29
    • 2018-11-17
    • 2018-06-29
    • 1970-01-01
    相关资源
    最近更新 更多