【问题标题】:How to test timeout() in a rxjs pipe with jasmine-marbles如何使用 jasmine-marbles 在 rxjs 管道中测试 timeout()
【发布时间】:2019-09-13 02:55:43
【问题描述】:

我写了一个过滤输入 observable 的管道。在管道中,如果源未及时发出预期值,我使用 timeout() 运算符指定超时以中止等待。 我想用 jasmine-marbles 测试超时情况,但我无法让它工作。 我相信 expect(source).toBeObservable() 在源发射之前进行评估。

Stackblitz

要测试的管道:

source = cold('a', { a: { id: 'a' } }).pipe(
  timeout(500),
  filter((a) => false),
  catchError((err) => {
    return of({ timeout: true })
  }),
  take(1)
);

使用 toPromise() 进行测试按预期工作:

expect(await source.toPromise()).toEqual({ timeout: true });

用 jasmine-marbles 测试

const expected = cold('500ms (a|)', { a: { timeout: true } });
expect(source).toBeObservable(expected);

因错误而失败

Expected $.length = 0 to equal 2.
Expected $[0] = undefined to equal Object({ frame: 500, notification: Notification({ kind: 'N', value: Object({ timeout: true }), error: undefined, hasValue: true }) }).
Expected $[1] = undefined to equal Object({ frame: 500, notification: Notification({ kind: 'C', value: undefined, error: undefined, hasValue: false }) }).

【问题讨论】:

    标签: unit-testing rxjs timeout jasmine-marbles


    【解决方案1】:

    最近在 jasmine-marbles 0.5.0 中添加了对时间进展的支持 (see jasmine-marbles PR #38)。附加的测试规范被添加到包中,展示了实现您想要的几种可能方法中的一种。以下是我能够使用您的 Stackblitz 示例汇总的一些选项。

    选项 1

    当您在测试方法之外(例如在beforeEach 中)初始化源可观察对象时,您必须显式初始化并将测试调度程序传递给timeout 以使expect().toBeObservable() 工作。但是,请注意,此更改将破坏“应该与 toPromise 一起使用”测试。 (我不知道为什么,但toPromise() 似乎不适用于这种方法。)

    describe('Marble testing with timeout', () => {
    
      let source;
    
      beforeEach(() => {
        // You must explicitly init the test scheduler in `beforeEach`.
        initTestScheduler()
        source = cold('a', { a: { id: 'a' } }).pipe(
          // You must explicitly pass the test scheduler.
          timeout(500, getTestScheduler()),
          filter((a) => false),
          catchError(err => {
            return of({ timeout: true })
          }),
          take(1)
        );
      });
    
      it('should work with toBeObservable', () => {
        const expected = cold('500ms (a|)', { a: { timeout: true } });
        expect(source).toBeObservable(expected);
      });
    });
    

    选项 2

    您可以稍微重构一些东西并在测试方法中初始化可观察的源(not in beforeEach)。您不需要显式初始化测试调度程序(jasmine-marbles 会在测试方法运行之前为您完成),但您仍然必须将其传递给timeout。请注意createSource 函数如何与测试调度程序或默认调度程序一起使用(如果scheduler 参数保留为undefined)。此选项适用于“应该使用 toPromise”测试和“应该使用 toBeObservable”测试。

    describe('Marble testing with timeout', () => {
    
      const createSource = (scheduler = undefined) => {
        return cold('a', { a: { id: 'a' } }).pipe(
          // You must explicitly pass the test scheduler (or undefined to use the default scheduler).
          timeout(500, scheduler),
          filter((a) => false),
          catchError(err => {
            return of({ timeout: true })
          }),
          take(1)
        );
      };
    
      it('should work with toPromise', async () => {
        const source = createSource();
        expect(await source.toPromise()).toEqual({ timeout: true });
      });
    
      it('should work with toBeObservable', () => {
        const source = createSource(getTestScheduler());
        const expected = cold('500ms (a|)', { a: { timeout: true } });
        expect(source).toBeObservable(expected);
      });
    });
    

    选项 3

    最后,如果您明确使用测试调度程序的run 方法,您可以跳过将测试调度程序传递给timeout,但您必须使用expectObservable(而不是expect().toBeObservable()。它工作得很好,但是Jasmine将报告警告“SPEC HAS NO EXPECTATIONS”。

    describe('Marble testing with timeout', () => {
    
      let source;
    
      beforeEach(() => {
        source = cold('a', { a: { id: 'a' } }).pipe(
          timeout(500),
          filter((a) => false),
          catchError(err => {
            return of({ timeout: true })
          }),
          take(1)
        );
      });
    
      it('should work with scheduler and expectObservable', () => {
        const scheduler = getTestScheduler();
        scheduler.run(({ expectObservable }) => {
          expectObservable(source).toBe('500ms (0|)', [{ timeout: true }]);
        });
      });
    });
    

    【讨论】:

    • 看起来时间进程仍在融入jasmine-marbles。我想我会插入rxjs-marbles。使用它的良好体验。它支持 Jasmine(以及其他),并且它们为各种用例提供​​了大量示例。 github.com/cartant/rxjs-marbles/tree/master/examples
    • 谢谢@seniorquico,很好的回应!与 Stackblitz 示例相反,我无法让选项 1 在我的生产代码中工作,我不知道为什么。但无论如何我更喜欢选项 3,因为它不涉及在生产代码中处理调度程序。旁注:我用 expect().nothing() 避免了 Jasmine 警告
    猜你喜欢
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 2018-05-15
    • 2018-01-04
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多