带延迟测试 NGRX 效果

2024-01-03

我想测试一下效果如下:

  1. 如果调度了 LoadEntriesSucces 操作,则效果开始
  2. 等待5秒
  3. 5 秒后发送 http 请求
  4. 当响应到达时,将调度新的操作(取决于响应是成功还是错误)。

Effect 的代码如下所示:

  @Effect()
  continuePollingEntries$ = this.actions$.pipe(
    ofType(SubnetBrowserApiActions.SubnetBrowserApiActionTypes.LoadEntriesSucces),
    delay(5000),
    switchMap(() => {
      return this.subnetBrowserService.getSubnetEntries().pipe(
        map((entries) => {
          return new SubnetBrowserApiActions.LoadEntriesSucces({ entries });
        }),
        catchError((error) => {
          return of(new SubnetBrowserApiActions.LoadEntriesFailure({ error }));
        }),
      );
    }),
  );

我想测试的是5秒后是否发出效果:

it('should dispatch action after 5 seconds', () => {
  const entries: SubnetEntry[] = [{
    type: 'type',
    userText: 'userText',
    ipAddress: '0.0.0.0'
  }];

  const action = new SubnetBrowserApiActions.LoadEntriesSucces({entries});
  const completion = new SubnetBrowserApiActions.LoadEntriesSucces({entries});

  actions$ = hot('-a', { a: action });
  const response = cold('-a', {a: entries});
  const expected = cold('- 5s b ', { b: completion });

  subnetBrowserService.getSubnetEntries = () => (response);

  expect(effects.continuePollingEntries$).toBeObservable(expected);
});

然而这个测试对我不起作用。测试的输出如下所示:

Expected $.length = 0 to equal 3.
Expected $[0] = undefined to equal Object({ frame: 20, notification: Notification({ kind: 'N', value: undefined, error: undefined, hasValue: true }) }).
Expected $[1] = undefined to equal Object({ frame: 30, notification: Notification({ kind: 'N', value: undefined, error: undefined, hasValue: true }) }).
Expected $[2] = undefined to equal Object({ frame: 50, notification: Notification({ kind: 'N', value: LoadEntriesSucces({ payload: Object({ entries: [ Object({ type: 'type', userText: 'userText', ipAddress: '0.0.0.0' }) ] }), type: '[Subnet Browser API] Load Entries Succes' }), error: undefined, hasValue: true }) }).

我应该怎么做才能使这个测试成功?


正如另一个答案中提到的,测试该效果的一种方法是使用测试调度程序 https://rxjs-dev.firebaseapp.com/guide/testing/marble-testing但可以用更简单的方式完成。

我们可以测试我们的异步 RxJS 代码同步地并通过使用 TestScheduler 虚拟化时间来确定。 ASCII 弹珠图为我们提供了一种直观的方式来表示 Observable 的行为。我们可以使用它们来断言特定 Observable 的行为符合预期,以及创建可用作模拟的热 Observable 和冷 Observable。

例如我们单元测试一下如下效果:

effectWithDelay$ = createEffect(() => {
  return this.actions$.pipe(
    ofType(fromFooActions.doSomething),
    delay(5000),
    switchMap(({ payload }) => {
      const { someData } = payload;

      return this.fooService.someMethod(someData).pipe(
        map(() => {
          return fromFooActions.doSomethingSuccess();
        }),
        catchError(() => {
          return of(fromFooActions.doSomethinfError());
        }),
      );
    }),
  );
});

该效果仅在初始操作后等待 5 秒,然后调用一个服务,然后该服务将分派成功或错误操作。单元测试该效果的代码如下:

import { TestBed } from "@angular/core/testing";

import { provideMockActions } from "@ngrx/effects/testing";

import { Observable } from "rxjs";
import { TestScheduler } from "rxjs/testing";

import { FooEffects } from "./foo.effects";
import { FooService } from "../services/foo.service";
import * as fromFooActions from "../actions/foo.actions";

// ...

describe("FooEffects", () => {
  let actions$: Observable<unknown>;

  let testScheduler: TestScheduler; // <-- instance of the test scheduler

  let effects: FooEffects;
  let fooServiceMock: jasmine.SpyObj<FooService>;

  beforeEach(() => {
    // Initialize the TestScheduler instance passing a function to
    // compare if two objects are equal
    testScheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });

    TestBed.configureTestingModule({
      imports: [],
      providers: [
        FooEffects,
        provideMockActions(() => actions$),

        // Mock the service so that we can test if it was called
        // and if the right data was sent
        {
          provide: FooService,
          useValue: jasmine.createSpyObj("FooService", {
            someMethod: jasmine.createSpy(),
          }),
        },
      ],
    });

    effects = TestBed.inject(FooEffects);
    fooServiceMock = TestBed.inject(FooService);
  });

  describe("effectWithDelay$", () => {
    it("should dispatch doSomethingSuccess after 5 seconds if success", () => {
      const someDataMock = { someData: Math.random() * 100 };

      const initialAction = fromFooActions.doSomething(someDataMock);
      const expectedAction = fromFooActions.doSomethingSuccess();
    
      testScheduler.run((helpers) => {

        // When the code inside this callback is being executed, any operator 
        // that uses timers/AsyncScheduler (like delay, debounceTime, etc) will
        // **automatically** use the TestScheduler instead, so that we have 
        // "virtual time". You do not need to pass the TestScheduler to them, 
        // like in the past.
        // https://rxjs-dev.firebaseapp.com/guide/testing/marble-testing

        const { hot, cold, expectObservable } = helpers;

        // Actions // -a-
        // Service //    -b|
        // Results // 5s --c

        // Actions
        actions$ = hot("-a-", { a: initialAction });

        // Service
        fooServiceMock.someMethod.and.returnValue(cold("-b|", { b: null }));

        // Results
        expectObservable(effects.effectWithDelay$).toBe("5s --c", {
          c: expectedAction,
        });
      });

      // This needs to be outside of the run() callback
      // since it's executed synchronously :O
      expect(fooServiceMock.someMethod).toHaveBeenCalled();
      expect(fooServiceMock.someMethod).toHaveBeenCalledTimes(1);
      expect(fooServiceMock.someMethod).toHaveBeenCalledWith(someDataMock.someData);
    });
  });
});

请注意,在我使用的代码中expectObservable使用 TestScheduler 实例中的“虚拟时间”来测试效果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带延迟测试 NGRX 效果 的相关文章

  • RXJS 等待所有 Observable 完成并返回结果

    我正在尝试创建一个 RX 流 该流将异步执行 XHR 调用列表 然后等待它们完成 然后再进行下一个调用 为了帮助解释这一点 可以在普通 JS 中这样写 try await requests map r gt angularHttpServi
  • Jasmine 监视 RxJS 5.5 操作员

    我正在尝试使用 Jasmine 监视 RxJS 操作员 在我的测试中有不同的用例 我希望控制 Observable 返回的内容 为了说明我正在尝试做的事情 我创建了上面的示例 尽管我认为它没有多大意义 因为此可观察值总是返回相同的硬编码字符
  • Angular 模板中可观察对象上的 ObjectUnsubscribedErrorImpl

    我正在使用 Angular 11 并且正在使用以下命令访问组件模板中的可观察对象async pipe 路线的第一次加载 一切都工作得很好 没有错误 当我离开该页面并返回时 出现以下错误 组件模板 成分 import Component On
  • 如何在异步管道上使用自定义管道?

    我正在尝试在异步管道上创建自定义管道 我尝试了很多解决方案 但仍然不起作用 这是代码片段 Product sort ts 自定义管道 import PipeTransform Pipe from angular core import Ob
  • Angular 6 ngrx,如何将新项目添加到状态对象中的数组?

    我有一个简单的情况 我有像 CreateUser CreateSuccess CreateFail 这样的操作 我应该如何向数组添加新对象以及何时Create已调度操作或CreateSuccess 我应该怎么做 export functio
  • 来自 unpkg 的 rxjs 捆绑包

    我有一个 Angular 2 0 0 2 2 0 中有同样的问题 项目 当前的开发版本生成超过 100 个 http 请求 这是因为它加载了非捆绑版本的 rxjs 当我有以下情况时 map our app is within the app
  • RxJS v5 中的速率限制和计数限制事件,但也允许传递

    我有很多事件要发送到服务 但请求有速率限制 每个请求都有计数限制 每秒 1 个请求 bufferTime 1000 每个请求 100 个活动项目 bufferCount 100 问题是 我不确定如何以有意义的方式将它们组合起来 允许通过 让
  • RxJs 很好的教程 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试学习ReactiveX 我花了差不多一整天的时间 还是没能正确理解 我尝试查看官方文档 但很
  • NgRx - 状态如何组合和初始化

    当我们初始化 Store 时 StoreModule provideStore r1 Reducer1 r2 Reducer2 我们确实将减速器传递到 Store 进行存储 但我们实际上从未将初始状态传递给存储 除了在减速器函数中定义它 c
  • Angular 自定义错误处理程序未从 Promise 获取错误类型

    当从承诺中抛出每个错误时 我的自定义错误处理程序都会丢失其类型 import HttpErrorResponse from angular common http import ErrorHandler Injectable Injecto
  • 从打字稿和 Angular 中的 Observable 获取字符串值

    我想从 Observable 获取字符串值并将该值从函数返回给调用者函数 例如 我有一组键 想一一获取所有键的值 字符串 并将其显示在具有菜单栏的 html 组件中 这是 ts 文件 key list component ts public
  • 使用 Jest 模拟延迟() RxJS

    有没有简单的方法来嘲笑delay 例如 带有假时间的可观察对象中的 RxJS 方法 我有这个方法 register user return this checkLog user delay 500 flatMap 当我删除时delay 方法
  • subject.next 不在 ngOnInit 中触发

    有谁知道为什么这段代码 从主题初始化一个值 不起作用 是否存在错误或设计使然 我究竟做错了什么 ts import Component OnInit from angular core import Subject from rxjs Co
  • RxJS 将三元组中的属性组合到表中

    我有一项服务生成类似于三元组的对象 它们将采用以下格式 country attribute value Example country usa attribute population value 100 country mexico at
  • Angular Template:如何绑定 RXJS Observable 并读取其属性?

    我创建了这个界面 interface IGame name string description string 我将它用作 Observable 并将其传递为Input to the Component Input public game
  • Pipe 和 Tap VS 使用 ngxs 订阅

    我正在玩管道并订阅 如果我使用带有水龙头的管道 则控制台中不会登录任何内容 如果我使用订阅 它就可以工作 那么我做错了什么 import Observable from rxjs import tap take from rxjs oper
  • 如何在 Angular2 中实现间隔/轮询以与量角器一起使用?

    我有一个 angular2 应用程序 我想用量角器进行测试 在此应用程序中 我有一个带有图表的页面 该页面正在使用自动生成的数据定期更新 显然 Protractor 的一项功能是在执行测试代码之前等待脚本和 http 调用完成 但是 如果有
  • Angular 2 Observable 具有多个订阅者

    我有一个 Angular 2 服务 可以从 API 获取数据 该服务有 3 个订阅者 在组件中定义 每个订阅者都对数据执行其他操作 不同的图表 我注意到我正在向 API 发出三个 GET 请求 而我想要实现的是一个请求 并且订阅者将共享数据
  • 如何在 RxJS 订阅方法中等待

    在 RxJS 主题的订阅回调内部 我想要await on an async功能 下面是打字稿转译器抱怨的代码示例 错误 131 21 TS2304 找不到名称 await async ngOnInit this subscriber dat
  • 如何使用rxjs的bindCallback

    我正在尝试将 bindCallback 用于 mqtt client connect event https www npmjs com package mqtt event connect https www npmjs com pack

随机推荐