Ngrx测试-如何配置TestBed以便在测试中实例化整个状态存储 [英] Ngrx testing - How to configure TestBed in order to instantiate the entire state store in a test

查看:93
本文介绍了Ngrx测试-如何配置TestBed以便在测试中实例化整个状态存储的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Angular 5应用中为ngrx 4状态存储运行一些集成测试.我的愿望是一次一起测试调度ngrx动作,效果,webapi,模拟服务器和选择器.为效果和还原剂编写单独的测试将是一个耗时的工作,我需要努力一些.我只需要知道我的页面之一何时出现故障,而不是确切的位置.

I want to run some integration tests for the ngrx 4 state store in an Angular 5 app. My desire is to test dispatching ngrx actions, effects, webapi, mock server and selectors all together in one shot. Writting separate tests for effects and reducers is going to be a time sink and I need to cut some corners. I only need to know when one of my pages fails not the exact spot.

我在上一个项目中设法将Redux与Angular 2一起使用时做到了这一点.这很容易设置.但是,使用ngrx会遇到一些麻烦.运行TestBed时,以某种方式无法正确地将AccountsWebapi注入到AccountsEffects类中.而不是接收webapi的实例,似乎我正在获取构造函数.同时,似乎已在_AccountsService中正确注入并实例化了相同的webapi.

I managed to do this in a previous project while using redux together with Angular 2. That was quite easy to setup. However, with ngrx I have some trouble. Somehow AccountsWebapi is not properly injected in AccountsEffects class when running the TestBed. Instead of receiving an instance of the webapi, it looks like I'm getting the constructor. At the same time, the same webapi seems to be injected and instantiated properly in _AccountsService.

accounts.service.spec.ts

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);
    });

accounts.effects.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)()
    }

控制台日志

推荐答案

首次尝试.不要这样!阅读下面的

最后,我找到了一种运行这种测试的方法.我100%肯定有更好的方法来做到这一点.在我找到它之前,这是必须要做的.

Finally, I found a way to get this kind of test running. I am 100% sure there is a far better way to do it. Until I find it, this has to do.

/** 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.我知道这与事实不符.我已经就什么是最佳测试方法进行了相当多的研究.我仍然发现对于我正在构建的那种应用程序,运行集成测试比单元测试更合适.

Finally, I can get an instance of the webapi in the effects capable of running real requests. Now all I have to do is to mock a webapi. I know it goes against the grain. I've done my fair share of research on what is the best way to test. I still find it more suitable to run integration tests rather than unit tests for the kind of app I'm building.

编辑(正确答案) 我将保留前面的答案,只是为了证明我愿意忽略代码的气味...

Edit (the proper answer) I will keep the previous answer just to demonstrate how willing I was to ignore a code smell...

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

Due to some misunderstanding about how to configure the TestBed the AccountsWebapi constructor was delivered instead of the instance. That was because I was using { provider: AccountsWebapi, useValue: AccountsWebapi }. The proper setup is to make use of useClass as demonstrated here { provider: AccountsWebapi, useClass: AccountsWebapi }. When using a class the dependency injection framework will trigger the construction step and it will inject the proper dependencies as longs as they are configured properly.

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

Another issue was that the AuthHttp service was not provided in AccountsWebapi. This was easily solved by declaring the HttpModule as a dependency in the testBed imports. Also, this was a good opportunity for me to learn that I have several tightly coupled services that need to be provided together in the TestBed. In the future, I need to reduce the number of dependencies between services or loosen them by using events.

完成所有这些修改后,可以轻松实例化TestBed,而无需将EffectClass包装在负责初始化AccountWebapi的另一层中.由于对依赖项注入框架缺乏正确的了解,因此该解决方案的设计过度了.

After finishing all these modifications the TestBed can be easily instantiated without the need to wrap the EffectsClass in another layer that takes care of initing the AccountWebapi. That was a grossly overengineered solution due to lack of proper understanding of the dependency injection framework.

这篇关于Ngrx测试-如何配置TestBed以便在测试中实例化整个状态存储的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆