Ngrx测试 - 如何配置TestBed以在测试中实例化整个状态存储

时间:2018-04-17 08:11:59

标签: angular ngrx ngrx-effects ngrx-store-4.0

我想在Angular 5应用程序中为ngrx 4状态存储运行一些集成测试。我的愿望是一次性测试调度ngrx动作,效果,webapi,模拟服务器和选择器。为效果和减速器编写单独的测试将是一个时间下沉,我需要削减一些角落。我只需要知道我的一个页面何时失败并不是确切的位置。

我设法在以前的项目中使用redux和Angular 2一起使用。这很容易设置。但是,对于ngrx我有一些麻烦。运行AccountsWebapi时,AccountsEffects类中TestBed未正确注入_AccountsService。看起来我得到的是构造函数,而不是接收webapi的实例。同时,似乎在describe("AccountsService - ", () => { let accService: AccountsService; beforeAll( ()=> { TestBed.resetTestEnvironment(); TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); }); beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ StoreModule.forRoot({appReducer}), EffectsModule.forRoot([AccountsEffects]), ], declarations: [], providers: [ { provide: AccountsService, useValue: AccountsService }, { provide: AccountsWebapi, useValue: AccountsWebapi }, // ... Other dependencies ] }).compileComponents(); // Instantiation let _AccountsUtilsService = TestBed.get(AccountsUtilsService); let _store = TestBed.get(Store); let _AccountsService = TestBed.get(AccountsService); // ... Other dependencies accService = new _AccountsService( new _AccountsUtilsService(), new _AccountsWebapi(), _store, // ... Other dependencies ); })); it("Sample Test", () => { console.log(`Accounts SERVICE`, accService.accountsWebapi); accService.getAccountAsync(1); expect(1).toEqual(1); }); 中正确地注入并实例化了相同的webapi。

accounts.service.spec.ts

@Injectable()
export class AccountsEffects {

    constructor(
        private actions$: Actions,
        private store$: Store<AppState>,
        private accountsService: AccountsService,
        private accountsWebapi: AccountsWebapi,
    ) {
        console.log('Accounts EFFECTS', this.accountsWebapi)

        // Typing this line will instantiate the dependency...
        this.accountsWebapi = new (this.accountsWebapi as any)()
    }

accounts.effects.ts

this.chart.listen('rowSelect', function (event) {
      console.log(event['item'].get('name'));
    });

控制台日志

enter image description here

1 个答案:

答案 0 :(得分:1)

第一次尝试。不要这样做!阅读下面的内容

最后,我找到了一种让这种测试运行的方法。我百分百肯定有一个更好的方法来做到这一点。直到我找到它,这必须做。

/** Force initialise the web api in effects */
@Injectable()
class AccountsEffectsTest extends AccountsEffects {

    constructor(
        protected _actions$: Actions,
        protected _store$: Store<AppState>,
        protected _accountsWebapi: AccountsWebapi,
    ) {
        super(
            _actions$,
            _store$,
            _accountsWebapi,
        );

        // This line looks ugly but it spares a lot of imports
        // Certainly there is a better way then this
        let providers = ((new HttpModule()) as any).__proto__.constructor.decorators['0'].args['0'].providers;
        let injector = ReflectiveInjector.resolveAndCreate([
            ...providers
        ]);

        this.accountsWebapi = new (_accountsWebapi as any)(
            null,
            injector.get(Http)
        );

        console.log('Accounts EFFECTS', this.accountsWebapi);
    }

}

最后,我可以在能够运行实际请求的效果中获得webapi的实例。现在我所要做的就是模仿webapi。我知道这与谷物不符。我已经完成了关于什么是最好的测试方法的研究。我仍然觉得它更适合运行集成测试,而不是我正在构建的app类型的单元测试。

编辑(正确答案) 我将保留之前的答案,以证明我是多么愿意忽略代码气味......

TestBed可以实例化EffectsModule

由于对如何配置TestBed的一些误解,AccountsWebapi构造函数已交付而不是实例。那是因为我使用{ provider: AccountsWebapi, useValue: AccountsWebapi }。正确的设置是使用此处useClass演示的{ provider: AccountsWebapi, useClass: AccountsWebapi }。当使用类时,依赖注入框架将触发构造步骤,并且它将在正确配置时注入适当的依赖项。

另一个问题是AuthHttp未提供AccountsWebapi服务。通过将HttpModule声明为testBed导入中的依赖项,可以轻松解决此问题。此外,这是一个很好的机会让我了解到我需要在TestBed中提供几个紧密耦合的服务。将来,我需要减少服务之间的依赖关系数量,或者通过使用事件来放松它们。

完成所有这些修改后,可以轻松实例化TestBed,而无需将EffectsClass包装在另一个负责启动AccountWebapi的层中。由于缺乏对依赖注入框架的正确理解,这是一个严重过度设计的解决方案。