Ngrx 测试 - 如何配置 TestBed 以便在测试中实例化整个状态存储
Ngrx testing - How to configure TestBed in order to instantiate the entire state store in a test
我想 运行 在 Angular 5 应用程序中对 ngrx 4 状态存储进行一些集成测试。我的愿望是一次性测试调度 ngrx 动作、效果、webapi、模拟服务器和选择器。为 effects 和 reducers 编写单独的测试将是一个时间沉没,我需要偷工减料。我只需要知道我的某个页面何时失败而不是确切的位置。
我在之前的项目中将 redux 与 Angular 2 一起使用时设法做到了这一点。这很容易设置。但是,使用 ngrx 我遇到了一些麻烦。当 运行 连接 TestBed
时,AccountsWebapi
无法正确注入 AccountsEffects
class。看起来我正在获取构造函数,而不是接收 webapi 的实例。同时,似乎在 _AccountsService
.
中正确注入并实例化了相同的 webapi
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)()
}
控制台日志
第一次尝试。不要这样做!阅读下文
终于,我找到了获得这种测试的方法运行ning。我 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);
}
}
最后,我可以在能够 运行ning 实际请求的效果中获得 webapi 的实例。现在我所要做的就是模拟一个 webapi。我知道这有悖常理。我已经对什么是最好的测试方法进行了相当多的研究。我仍然发现它更适合 运行 集成测试,而不是我正在构建的那种应用程序的单元测试。
编辑(正确答案)
我将保留之前的答案只是为了证明我是多么愿意忽略代码味道...
TestBed 可以实例化 EffectsModule
由于对如何配置 TestBed 的一些误解,交付了 AccountsWebapi
构造函数而不是实例。那是因为我使用的是 { provider: AccountsWebapi, useValue: AccountsWebapi }
。正确的设置是使用 useClass
,如此处 { provider: AccountsWebapi, useClass: AccountsWebapi }
所示。当使用 class 时,依赖注入框架将触发构建步骤,并且只要配置正确,它就会注入正确的依赖项。
另一个问题是 AccountsWebapi
中没有提供 AuthHttp
服务。这很容易通过在 testBed 导入中声明 HttpModule
为依赖项来解决。此外,这是一个很好的机会,让我了解到我有几个紧密耦合的服务需要在 TestBed 中一起提供。以后需要通过事件来减少服务之间的依赖关系或者松散依赖关系。
完成所有这些修改后,可以轻松实例化 TestBed,而无需将 EffectsClass 包装在负责初始化 AccountWebapi
的另一层中。由于缺乏对依赖注入框架的正确理解,这是一个严重过度设计的解决方案。
我想 运行 在 Angular 5 应用程序中对 ngrx 4 状态存储进行一些集成测试。我的愿望是一次性测试调度 ngrx 动作、效果、webapi、模拟服务器和选择器。为 effects 和 reducers 编写单独的测试将是一个时间沉没,我需要偷工减料。我只需要知道我的某个页面何时失败而不是确切的位置。
我在之前的项目中将 redux 与 Angular 2 一起使用时设法做到了这一点。这很容易设置。但是,使用 ngrx 我遇到了一些麻烦。当 运行 连接 TestBed
时,AccountsWebapi
无法正确注入 AccountsEffects
class。看起来我正在获取构造函数,而不是接收 webapi 的实例。同时,似乎在 _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)()
}
控制台日志
第一次尝试。不要这样做!阅读下文
终于,我找到了获得这种测试的方法运行ning。我 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);
}
}
最后,我可以在能够 运行ning 实际请求的效果中获得 webapi 的实例。现在我所要做的就是模拟一个 webapi。我知道这有悖常理。我已经对什么是最好的测试方法进行了相当多的研究。我仍然发现它更适合 运行 集成测试,而不是我正在构建的那种应用程序的单元测试。
编辑(正确答案) 我将保留之前的答案只是为了证明我是多么愿意忽略代码味道...
TestBed 可以实例化 EffectsModule
由于对如何配置 TestBed 的一些误解,交付了 AccountsWebapi
构造函数而不是实例。那是因为我使用的是 { provider: AccountsWebapi, useValue: AccountsWebapi }
。正确的设置是使用 useClass
,如此处 { provider: AccountsWebapi, useClass: AccountsWebapi }
所示。当使用 class 时,依赖注入框架将触发构建步骤,并且只要配置正确,它就会注入正确的依赖项。
另一个问题是 AccountsWebapi
中没有提供 AuthHttp
服务。这很容易通过在 testBed 导入中声明 HttpModule
为依赖项来解决。此外,这是一个很好的机会,让我了解到我有几个紧密耦合的服务需要在 TestBed 中一起提供。以后需要通过事件来减少服务之间的依赖关系或者松散依赖关系。
完成所有这些修改后,可以轻松实例化 TestBed,而无需将 EffectsClass 包装在负责初始化 AccountWebapi
的另一层中。由于缺乏对依赖注入框架的正确理解,这是一个严重过度设计的解决方案。