NGRX - 使用 jasmine-marbles 将 Promise 转换为 observables 的测试效果问题

Posted

技术标签:

【中文标题】NGRX - 使用 jasmine-marbles 将 Promise 转换为 observables 的测试效果问题【英文标题】:NGRX - Problem with testing effects that convert promises to observables with jasmine-marbles 【发布时间】:2019-10-24 04:56:59 【问题描述】:

我有一个 nrwl/nx 工作区,其中有一个名为 missions-shared 的库。这个库有一个功能状态,在密钥 missions 下我还有一个后端服务,名为 BackendServiceClient此后端返回承诺(由于代码生成)

我的问题在于后端返回承诺,而 jasmine-marbles 无法识别:

我正在尝试模拟后端。我为此使用了一个测试提供者


let backend = 
  get: () => Promise.resolve(items: [])


TestBed.configureTestingModule(
...
providers : [
  provide: BackendServiceClient, useValue: backend
]
...

这是 MissionsEffect :


@Injectable()
export class MissionsEffects 
  @Effect() loadMissions$ = this.dataPersistence.fetch(
    MissionsActionTypes.LoadMissions,
    
      run: (action: LoadMissions, state: MissionsPartialState) => 
        return from(this.backend.get(new GetMissions())).pipe(
          map(r => new MissionsLoaded(r.items))
        );
      ,

      onError: (action: LoadMissions, error) => 
        console.error('Error', error);
        return new MissionsLoadError(error);
      
    
  );

  constructor(
    private dataPersistence: DataPersistence<MissionsPartialState>,
    private backend: BackendClientService
  ) 


然后我尝试使用 jasmine-marbles 测试这些@Effect

   actions = hot('-a-|',  a: new LoadMissions() );
   expect(effects.loadMissions$).toBeObservable(
     hot('-a-|',  a: new MissionsLoaded([]) )
   );

但我在测试中遇到了这个错误:

  ● MissionsEffects › loadMissions$ › should work

    expect(received).toEqual(expected) // deep equality

    - Expected
    + Received

    - Array [
    -   Object 
    -     "frame": 10,
    -     "notification": Notification 
    -       "error": undefined,
    -       "hasValue": true,
    -       "kind": "N",
    -       "value": MissionsLoaded 
    -         "payload": Array [],
    -         "type": "[Missions] Missions Loaded",
    -       ,
    -     ,
    -   ,
    -   Object 
    -     "frame": 30,
    -     "notification": Notification 
    -       "error": undefined,
    -       "hasValue": false,
    -       "kind": "C",
    -       "value": undefined,
    -     ,
    -   ,
    - ]
    + Array []

似乎是因为我正在使用 Promise 模拟后端(应该如此),它无法识别返回的 Observable。

如果我将模拟更改为:

let backend = 
  get: () => of(items: [])

然后测试成功。

这是测试:


describe('MissionsEffects', () => 
  let actions: Observable<any>;
  let effects: MissionsEffects;
  let backend = 
    get: () => of(items: [])
  

  beforeEach(() => 
    TestBed.configureTestingModule(
      imports: [
        NxModule.forRoot(),
        StoreModule.forRoot(),
        EffectsModule.forRoot([]),
        RouterModule.forRoot([])
      ],
      providers: [
        MissionsEffects,
        DataPersistence,
        provideMockActions(() => actions),
         provide: APP_BASE_HREF, useValue: '/' ,
         provide: BackendClientService, useValue: backend 
      ]
    );

    effects = TestBed.get(MissionsEffects);
  );

  describe('loadMissions$', () => 
    it('should work', () => 
      actions = hot('-a-|',  a: new LoadMissions() );
      expect(effects.loadMissions$).toBeObservable(
        hot('-a-|',  a: new MissionsLoaded([]) )
      );
    );
  );
);


我可以确认问题不是由于使用@nrwl/DataPersistence,因为以下@Effect 会产生相同的错误:


@Injectable()
export class MissionsEffects 

  @Effect() loadMissions$ = this.actions$
    .pipe(
      ofType(MissionsActionTypes.LoadMissions),
      switchMap(loadMission => 
        return from(this.backend.get(new GetMissions())
          .then(r => new MissionsLoaded(r.items))
          .catch(e => new MissionsLoadError(e)))
      )
  )

  constructor(
    private actions$: Actions,
    private backend: BackendClientService
  ) 


有人可以帮我准确理解这里的问题吗?为什么我不能使用模拟的 Promise 来测试这个?

【问题讨论】:

您曾经解决过这个问题吗?现在正在为类似的事情苦苦挣扎…… 【参考方案1】:

您可以尝试将您的测试包装在 async() 中,以确保完成所有异步调用。尽管resolve 是同步的,from 将添加一个.then 并且仍会等待它作为微任务解决。用asyncfakeAsync 包装你的it 函数可以解决这个问题。

【讨论】:

以上是关于NGRX - 使用 jasmine-marbles 将 Promise 转换为 observables 的测试效果问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ngrx-router-store 在 ngrx 效果中获取路由参数?

使用 @ngrx/entity 处理商店中的集合,getSelectors() 不起作用

为啥使用 NGRX 而不是构造函数注入服务?

我可以在 angular + ngrx 中使用对象扩展语法吗?

使用 NgRX 实体标准化 Api 响应

如何使用Ngrx / Effects发布http请求?