问题描述
我使用的是 angular 8。我有一个模块 app
和一个子模块“功能”。两者都有自己的商店。 'app.module.ts' 有
@NgModule({
imports: [
...
StoreModule.forRoot({ 'app': reducer }),...
}]
同理,'feature' 模块有
StoreModule.forFeature('feature',fromFeature.reducer)
现在,在 jasmine 中测试 app
级组件时,我模拟 Store 如下
let store: MockStore<State>;
Testbed.configureTestingModule({
imports: [
StoreModule.forRoot({'app' : reducer})
],providers: [
provideMockStore({initialState})
]
其中 initialState
是 app
模块的初始状态。但是,在尝试获取 store = Testbed.get(Store);
state
模块的 feature
的未定义值时出错,它尝试从 feature
模块访问选择器。
我的理解是,我需要使用整个应用程序的状态来初始化 mockStore,而不仅仅是 app
模块状态。但是,我不确定如何实现。
我确实尝试导入 StoreModule.forFeature('feature',featureReducer)
并将模拟存储初始化为 provideMockStore({initialState:: {initialState,featureInitialState}})
,但我仍然获得了选择器的功能状态 undefined
。
错误如下
ERROR TypeError: Cannot read property 'isEntity' of undefined
at getIsEntity (VM17 main.js:50252)
at store.js:583
at memoized (store.js:525)
at defaultStateFn (store.js:552)
at store.js:586
at memoized (store.js:525)
at MapSubscriber.project (store.js:480)
其中 getIsEntity
是一个 feature
模块选择器,定义如下:
const getIsEntity = (state: State): boolean => state.isEntity
如何模拟/传递父应用模块中功能模块的初始状态?
解决方法
将其模拟为根存储,因为没有自己的存储,所有减速器/动作和效果始终与存储(单个根存储)一起使用,而调用“forFeature”给人的印象是它们有自己的存储。
不,他们没有,他们访问同一个根存储。
let store: MockStore<State>;
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({
'app' : reducer,'feature': ....,})
],providers: [
provideMockStore({initialState})
]
不幸的是,由于信息量很少,很难指出确切的错误位置,但您可以在下面查看一个工作示例:
describe('suite',() => {
let fixture: MockedComponentFixture<TestedComponent>;
let storeState: DeepPartial<StoreState & {ngrxCorrelationId: {tasks: Array<string>; payloads: unknown}}>;
beforeEach(() => {
const actions = new Observable<{}>();
storeState = {
ngrxCorrelationId: {
tasks: [],payloads: {},},v2ProjectEntity: {
ids: [],entities: {},lists: {},v2PermissionsEntity: {},};
TestBed.configureTestingModule({
declarations: [TestedComponent],providers: [
provideMockStore({
initialState: storeState,}),provideMockActions(() => actions),{
provide: LoaderService,useValue: createSpyObj('LoaderService',['setLoaderStatus','hideLoader']),{
provide: Router,useValue: createSpyObj('Router',['navigate','navigateByUrl']),{
provide: ActivatedRoute,useValue: {
queryParams: new Subject(),TestedEffects,],});
fixture = MockRender(TestedComponent);
});
it('listens on initial load public projects of the smart component',inject(
[ActivatedRoute,Store],(activatedRoute: ActivatedRoute,store: Store<{}>) => {
const props = {
cid: 'public',options: {
relations: [
'location','location.address','quotations','quotations.owner','quotations.owner.company','quotations.documents','certificates','certificatesCustom','documents',scopes: ['public'],filters: {
category: undefined,deadline: undefined,order: undefined,};
spyOn(store,'dispatch');
(<Subject<{}>>activatedRoute.queryParams).next({});
fixture.detectChanges();
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(publicProjectsListLoad({...props}));
},));
it('listens on load public projects with filters of the smart component',filters: {
category: ['transformer'],deadline: ['0-10'],order: ['projectName:desc'],'dispatch');
(<Subject<{}>>activatedRoute.queryParams).next({
'filters[category]': 'transformer','filters[deadline]': '0-10',order: 'projectName:desc',});
fixture.detectChanges();
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(publicProjectsListLoad({...props}));
},));
});
,
我终于找到了问题所在。我有模块的单独状态,应用程序状态没有包含所有状态。因此,当我仅使用应用程序状态进行模拟时,测试还需要模块的模拟状态,而在我传递给模拟商店的初始状态中不存在该状态。
根据 Angular 8 文档,示例显示,应用商店状态应该包含所有模块的状态,而我的案例中没有。
通过创可贴解决了这个问题,我们创建了一个假的初始状态来扩展应用程序和模块状态。然后,将那个虚假的初始状态传递给模拟商店。