简体   繁体   English

使用 fakeAsync 测试 Observable,使用 Jasmine 延迟和滴答

[英]Test Observable with fakeAsync, delay and tick using Jasmine

I have a pipe that helps return the state of an observable.我有一个 pipe 可以帮助返回一个 observable 的 state。

import {Pipe, PipeTransform} from '@angular/core';
import {Observable, of} from 'rxjs';
import {catchError, map, startWith} from 'rxjs/operators';

/** Specifies the status of an Observable. */
export interface ObservableStatus<T> {
  loading?: boolean;
  value?: T;
  error?: boolean;
}

/** Returns the status {@code ObservableStatus} of a given Observable. */
@Pipe({name: 'getObserverStatus'})
export class ObservableStatusPipe implements PipeTransform {
  transform<T = Item>(observer: Observable<T>):
      Observable<ObservableStatus<T>> {
    return observer.pipe(
        map((value: T) => {
          return {
            loading: false,
            error: false,
            value,
          };
        }),
        startWith({loading: true}),
        catchError(error => of({loading: false, error: true})));
  }
}

I want to write unit tests for this functionality using Jasmine. I tried using fakeAsync, delay, tick, flush, discardPeriodicTasks but it doesn't seem to work.我想使用 Jasmine 为该功能编写单元测试。我尝试使用fakeAsync, delay, tick, flush, discardPeriodicTasks但它似乎不起作用。

I have tried different ways:我尝试了不同的方法:

  • Way 1方式一
describe('loading test', () => {
  const loadingPipe = new ObservableStatusPipe();

  it('returns state of an observable', fakeAsync(() => {
    const input: Observable<Item> = of({name: 'Item'}).pipe(delay(1000));

    const result: Observable<ObservableStatus<Item>> = loadingPipe.transform(input);

    result.subscribe(val => {
      expect(val.loading).toEqual(true);
      expect(val.value).toBeUndefined();
    });
    tick(2000);
    result.subscribe(val => {
      expect(val.loading).toEqual(false);
      expect(val.value!.name).toEqual('Item');
    });
  }));
});

The above test case fails with following failures:上述测试用例失败并出现以下故障:

Error: Expected true to equal false. (at expect(val.loading).toEqual(false))
Error: 1 periodic timer(s) still in the queue.
  • Way 2: From online, I saw that we can use flush to flush any pending tasks.方式2:从网上看到我们可以使用flush来刷新任何待处理的任务。
describe('loading test', () => {
  const loadingPipe = new ObservableStatusPipe();

  it('returns state of an observable', fakeAsync(() => {
    const input: Observable<Item> = of({name: 'Item'}).pipe(delay(1000));

    const result: Observable<ObservableStatus<Item>> = loadingPipe.transform(input);

    result.subscribe(val => {
      expect(val.loading).toEqual(true);
      expect(val.value).toBeUndefined();
    });
    tick(2000);
    result.subscribe(val => {
      expect(val.loading).toEqual(false);
      expect(val.value!.name).toEqual('Item');
    });
    flush();     // <----- here.
  }));
});

This is helping to resolve Error: 1 periodic timer(s) still in the queue.这有助于解决Error: 1 periodic timer(s) still in the queue. issue.问题。 However the test case still fails with:但是测试用例仍然失败:

Error: Expected true to equal false.
TypeError: Cannot read properties of undefined (reading 'name')

Does all this mean the tick is somehow not simulating time on input observable?所有这一切是否意味着tick以某种方式不模拟可观察到的input时间?

  • I tested the same simulation on input observable directly:我直接在input observable 上测试了相同的模拟:
describe('loading test', () => {
  const loadingPipe = new ObservableStatusPipe();

  it('returns state of an observable', fakeAsync(() => {
    const input: Observable<Item> = of({name: 'Item'}).pipe(delay(1000));

    input.subscribe(val => {
      expect(val.name).toBeUndefined();
    });
    tick(2000);
    input.subscribe(val => {
      expect(val.name).toEqual('Item');
    });
    discardPeriodicTasks();    <--- Using flush() here is causing 'Error: 2 periodic timer(s) still in the queue' error.
  }));
});

The above test case is passing.以上测试用例通过。 But I am still confused why flush() is not working here.但我仍然很困惑为什么 flush() 在这里不起作用。

describe('loading test', () => {
  const loadingPipe = new ObservableStatusPipe();

  it('returns state of an observable', fakeAsync(() => {
    const input: Observable<Item> = of({name: 'Item'}).pipe(delay(1000));

    const result: Observable<ObservableStatus<Item>> = loadingPipe.transform(input);

    result.subscribe(val => {
      expect(val.loading).toEqual(true);
      expect(val.value).toBeUndefined();
    });
    tick(2000);
    result.subscribe(val => {
      expect(val.loading).toEqual(false);
      expect(val.value!.name).toEqual('Item');
    });
    discardPeriodicTasks();
  }));
});

This still fails with the same errors:这仍然失败并出现相同的错误:

Error: Expected true to equal false. (at expect(val.loading).toEqual(false))
Error: 1 periodic timer(s) still in the queue.

Can someone explain what is happening here, and how to solve this, please?有人可以解释这里发生了什么,以及如何解决这个问题吗?

Btw, I do not want to use debounceTime , setTimeOut to solve this problem.顺便说一句,我不想使用debounceTimesetTimeOut来解决这个问题。 Because they do not seem to simulate time, instead actually waits and delays time ie using debounceTime(1000) will actually wait for 1 sec.因为它们似乎没有模拟时间,而是实际等待和延迟时间,即使用 debounceTime(1000) 实际上会等待 1 秒。 I do not want that in my case (I want to simulate time).我不想这样(我想模拟时间)。

  • Using delay operator while subscribing the observable is working (without using tick ).在订阅可观察对象时使用delay运算符是有效的(不使用tick )。
describe('loading test', () => {
  const loadingPipe = new ObservableStatusPipe();

  it('returns state of an observable', fakeAsync(() => {
       const input: Observable<Item> = of({name: 'Item'}).pipe(delay(1000));

       const result: Observable<ObservableStatus<Item>> = loadingPipe.transform(input);

       result.subscribe(val => {
         expect(val.loading).toEqual(true);
         expect(val.value).toBeUndefined();
       });
       result.pipe(delay(2000)).subscribe(val => {
         expect(val.loading).toEqual(false);
         expect(val.value!.name).toEqual('Item');
       });
       discardPeriodicTasks();
     }));
});

Is this actually delaying/waiting for 1000 or 2000ms, or does using fakeAsync somehow let delay to simulate the time?这实际上是延迟/等待 1000 或 2000 毫秒,还是使用 fakeAsync 以某种方式让延迟模拟时间?

Thanks!谢谢!

I think your way 1 is good but you may be facing a situation of late subscribers and multiple subscriptions.我认为你的方式 1 很好,但你可能面临延迟订阅和多个订阅的情况。 Try this:试试这个:

    // Take 2 emissions
    result.pipe(take(2)).subscribe(val => {
      if (val.loading) {
        console.log('False case');
        expect(val.loading).toEqual(true);
        expect(val.value).toBeUndefined();
      } else {
        console.log('True case');
        expect(val.loading).toEqual(false);
        expect(val.value!.name).toEqual('Item');
      }
    });
    
    // Move time in a fake way. The above observable stream should hopefully emit 
    // twice and therefore there is a take(2).
    // Make sure you see both console.logs to ensure both paths were asserted.
    tick(2000);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 tick 和 fakeAsync 单元测试 Observable 的角度组件 - Unit Test angular component for Observable using tick and fakeAsync Angular 使用 fakeAsync 进行单元测试,并且不等待代码使用 Observable 执行 - Angular unit test with fakeAsync and tick not waiting for code to execute with Observable 茉莉花fakeAsync滴答声不等待承诺被解决 - Jasmine fakeAsync tick doesn't wait for promise to be resolved Angular 2 fakeAsync 在使用 tick() 的函数中等待超时? - Angular 2 fakeAsync waiting for timeout in a function using tick()? 我正在使用 fakeAsync 并在 angular 的单元测试用例中打勾,但仍然在队列中出现错误 2 定期计时器 - I am using fakeAsync and tick in unit test case of angular , getting error 2 periodic timer(s) still in the queue Jasmine 使用弹珠测试多个可观察值 - Jasmine using marbles to test multiple observable values 错误:fakeAsync() 测试助手需要 zone-testing.js 但找不到。 使用 serenityJS+Jasmine 在 Angular webapp 上运行测试时 - Error: zone-testing.js is needed for the fakeAsync() test helper but could not be found. When running test on Angular webapp using serenityJS+Jasmine 使用 typescript、karma 和 jasmine 对 RxJS Observable.timer 进行单元测试 - Unit Test RxJS Observable.timer using typescript, karma and jasmine 如何使用茉莉花对 observable 进行单元测试 - How to unit test an observable with jasmine 如何在 fakeAsync() 中检测丢失的 tick() - How to detect missing tick() in fakeAsync()
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM