1

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

我在以前的项目中成功地做到了这一点,同时将 redux 与 Angular 2 一起使用。这很容易设置。但是,使用 ngrx 我遇到了一些麻烦。运行. AccountsWebapi_ AccountsEffects_ TestBed看起来我没有收到 webapi 的实例,而是得到了构造函数。同时,相同的 webapi 似乎在_AccountsService.

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

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

控制台日志

在此处输入图像描述

4

1 回答 1

1

第一次尝试。不要这样做!阅读下文

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

/** 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。我知道这违背了粮食。我已经对什么是最好的测试方法进行了相当多的研究。对于我正在构建的应用程序,我仍然发现它更适合运行集成测试而不是单元测试。

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

TestBed 可以实例化 EffectsModule

由于对如何配置 TestBed 的一些误解,AccountsWebapi交付的是构造函数而不是实例。那是因为我在使用{ provider: AccountsWebapi, useValue: AccountsWebapi }. 正确的设置是使用useClass这里演示的{ provider: AccountsWebapi, useClass: AccountsWebapi }。使用类时,依赖注入框架将触发构造步骤,只要配置正确,它就会注入正确的依赖。

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

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

于 2018-04-18T13:36:59.313 回答