尝试运行 Angular 测试时出现 NullInjectorError: Noprovider for Store 错误

问题描述 投票:0回答:2

app.component.ts

...
constructor(private store: Store<fromAppState.AppState>) {}
  ngOnInit() {
    this.store.dispatch(new fromAppActions.Load());
...

app.state.ts

export interface AppState {
  structure: Structure;
  buttons: number[];
  bars: Bar[];
  limit: number;
  isLoading: boolean;
  error: string;
}
export const initialState: AppState = {
  buttons: [],
  structure: null,
  bars: [],
  limit: 0,
  isLoading: false,
  error: '',
};
const getState = createFeatureSelector<AppState>('myApp');
...

app.component.spec.ts

 describe('AppComponent', () => {
  @Component({
    selector: 'app-loader',
    template: '<div></div>',
  })
  class FakeLoaderComponent {
    @Input() loading: boolean;
  }
  let component: AppComponent;
  let mockStore: MockStore;
  let mockBarsSelector: MemoizedSelector<fromAppState.AppState, Bar[]>;
  // const storeMock = jasmine.createSpyObj('Store', ['select']);
  let buttons$: Observable<number[]> = of([45, 23, -8, -12]);
  let buttons: number[] = [45, 23, -8, -12];
  let loading$: Observable<boolean> = of(true);
  let structure: Structure = {
    buttons: [45, 23, -8, -12],
    bars: [15, 34, 7, 87],
    limit: 150,
  };
  let barsObject: Bar[] = [
    { value: 15, newValue: Math.round(15 * 100) / 150 },
    { value: 34, newValue: Math.round(34 * 100) / 150 },
    { value: 7, newValue: Math.round(7 * 100) / 150 },
    { value: 87, newValue: Math.round(87 * 100) / 150 },
  ];
  
  let fixture: ComponentFixture<AppComponent>;
  beforeEach(async(() => {
    fixture = TestBed.createComponent(AppComponent);
   
    component = fixture.componentInstance;

    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule,
        MaterialModule,
        StoreModule.forRoot({}, {}),
      ],
      declarations: [AppComponent, FakeLoaderComponent],
      providers: [provideMockStore()],
    }).compileComponents();
    component.bars = barsObject;
    component.limit = structure.limit;
    component.selectedProgressBar = -1;
    component.componentActive = true;
    component.buttons$ = buttons$;
    component.loading$ = loading$;

    mockStore = TestBed.inject(MockStore);
    mockStore.overrideSelector(fromAppState.getIsLoading, false);
    mockStore.overrideSelector(fromAppState.getBars, barsObject);
    mockStore.overrideSelector(fromAppState.getButtons, buttons);
    mockStore.overrideSelector(fromAppState.getStructure, structure);
    mockStore.overrideSelector(fromAppState.getError, '');
    mockBarsSelector = mockStore.overrideSelector(
      fromAppState.getBars,
      barsObject
    );
    fixture.detectChanges();
  }));

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });
});

app.module.ts

@NgModule({
  declarations: [AppComponent, LoaderComponent],

  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MaterialModule,
    HttpClientModule,
    StoreModule.forRoot({}, {}),
    EffectsModule.forRoot([AppEffects]),
    StoreModule.forFeature('myApp', AppReducer),
    StoreDevtoolsModule.instrument({
      name: 'My DevTools',
      maxAge: 50,
      logOnly: environment.production,
    }),
  ],
  providers: [AppService],
  bootstrap: [AppComponent],
})
export class AppModule {}

当我运行

ng test
时,我收到此错误: 失败:R3InjectorError(DynamicTestModule)[存储->存储]: NullInjectorError:没有商店提供者! 错误属性:Object({ ngTempTokenPath: null, ngTokenPath: [ 'Store', 'Store' ] }) 在茉莉花

我还需要将reducer导入到spec文件中吗?

angular unit-testing redux ngrx angular-test
2个回答
1
投票

只需在providers中添加一个空对象的providedMockStore

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [PuntersClubJoinClubPageComponent],
    imports: [HttpClientTestingModule],
    providers: [provideMockStore({})],
  }).compileComponents();
}));

0
投票

配置 TestBed 后尝试创建组件。

    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule,
        MaterialModule,
        StoreModule.forRoot({}, {}),
      ],
      declarations: [AppComponent, FakeLoaderComponent],
      providers: [provideMockStore()],
    }).compileComponents();


// do this AFTER configuration

fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
    

正如 Andrei 在评论中提到的,你只能使用真正的 Store 或

provideMockStore
- 不可能同时使用两者,据我所知,这可能会导致奇怪的行为。

© www.soinside.com 2019 - 2024. All rights reserved.