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
并且仍会等待它作为微任务解决。用async
或fakeAsync
包装你的it
函数可以解决这个问题。
【讨论】:
以上是关于NGRX - 使用 jasmine-marbles 将 Promise 转换为 observables 的测试效果问题的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ngrx-router-store 在 ngrx 效果中获取路由参数?
使用 @ngrx/entity 处理商店中的集合,getSelectors() 不起作用