Jasmine compile/create 组件与 beforeAll/beforeEach 行为异常
Jasmine compile/create components acting weirdly with beforeAll/beforeEach
我正在为我的应用程序编写一些单元测试,我遇到了一个非常奇怪的情况。
我构建了一个 angular 应用程序,我正在使用 karma&jasmine 进行 unit/integration 测试。
这是交易:
我写了这段代码
describe("Body Container component", () => {
let component: BodyContainerComponent;
let fixture: ComponentFixture<BodyContainerComponent>;
beforeEach(async () => {
getTestBed().configureTestingModule({
imports: [AppTestingModule]
});
await getTestBed().compileComponents();
fixture = getTestBed().createComponent(BodyContainerComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
它工作正常,测试被正确编译和执行(并通过)。
不过他们确实需要一段时间,我猜是因为每次都重新配置 testingModule 并编译组件。
所以 a 尝试以这种方式更改它:
beforeAll(async () => {
getTestBed().resetTestingModule();
getTestBed().configureTestingModule({
imports: [AppTestingModule]
});
await getTestBed().compileComponents();
});
beforeEach(async () => {
fixture = getTestBed().createComponent(BodyContainerComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
虽然在 getTestBed().createComponent 上调用失败,但此版本的 logic/API 调用失败,失败原因为:
Error: Illegal state: Could not load the summary for directive BodyContainerComponent.
error properties: Object({ ngSyntaxError: true })
at syntaxError (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:2430:1)
at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getDirectiveSummary (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:18535:1)
at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler.getComponentFactory (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:25979:1)
at CompilerImpl.push../node_modules/@angular/platform-browser-dynamic/fesm5/platform-browser-dynamic.js.CompilerImpl.getComponentFactory (http://localhost:9876/node_modules/@angular/platform-browser-dynamic/fesm5/platform-browser-dynamic.js?:162:1)
at TestingCompilerImpl.push../node_modules/@angular/platform-browser-dynamic/fesm5/testing.js.TestingCompilerImpl.getComponentFactory (http://localhost:9876/node_modules/@angular/platform-browser-dynamic/fesm5/testing.js?:110:1)
at TestBedViewEngine.push../node_modules/@angular/core/fesm5/testing.js.TestBedViewEngine.createComponent (http://localhost:9876/node_modules/@angular/core/fesm5/testing.js?:1905:1)
at Object.<anonymous> (http://localhost:9876/src/test/integration/app/components/body-container.integration-spec.ts?:21:32)
at step (http://localhost:9876/node_modules/tslib/tslib.es6.js?:97:1)
at Object.next (http://localhost:9876/node_modules/tslib/tslib.es6.js?:78:45)
at http://localhost:9876/node_modules/tslib/tslib.es6.js?:71:1
我尝试了各种迭代和更改以使其工作,但错误仍然存在。
经过一些分析,我可以说:
1- async/await 工作正常,只有在 beforeAll 完成后才调用 beforeach
2-每一个测试都给出上面的错误,即使是第一个(这很奇怪,因为逻辑上第一个不应该在两个版本之间改变一英寸
我注意到的一件事是第一个版本测试是随机执行的,而第二个版本似乎是按顺序执行的。
第二个版本的 beforach 上的异步没有改变任何东西,我也尝试调用各种 reset/destroy 方法但 none 似乎有所贡献。
你知道为什么会这样吗?
解决不了也没关系,纵观整个构建过程,毕竟是一个小优化,我比较好奇为什么会这样。
编辑:经过额外分析,通过查看 testBed 实例,我注意到 "beforeEach only" 实现具有
TestBedViewEngine{_instantiated: false, _compiler: TestingCompilerImpl{_compiler: CompilerImpl{_metadataResolver: ..., _delegate: ..., injector: ...}, _directiveResolver: MockDirectiveResolver{_reflector: ..., _directives: ...}, _pipeResolver: MockPipeResolver{_reflector: ..., _pipes: ...}, _moduleResolver: MockNgModuleResolver{_reflector: ..., _ngModules: ...}, _overrider: MetadataOverrider{_references: ...}}, _moduleRef: null, _moduleFactory: NgModuleFactory_{moduleType: function DynamicTestModule() { ... }, _bootstrapComponents: [], _ngModuleDefFactory: function(_l) { ... }}, _compilerOptions: [], _moduleOverrides: [], _componentOverrides: [], _directiveOverrides: [], _pipeOverrides: [], _providers: [], _declarations: [], _imports: [function AppTestingModule() { ... }], _schemas: [], _activeFixtures: [], _testEnvAotSummaries: function () { ... }, _aotSummaries: [], _templateOverrides: [], _isRoot: true, _rootProviderOverrides: [], platform: PlatformRef{_injector: StaticInjector{parent: ..., source: ..., _records: ...}, _modules: [], _destroyListeners: [], _destroyed: false}, ngModule: function BrowserDynamicTestingModule() { ... }}
而 beforeAll 有:
TestBedViewEngine{_instantiated: false, _compiler: null, _moduleRef: null, _moduleFactory: null, _compilerOptions: [], _moduleOverrides: [], _componentOverrides: [], _directiveOverrides: [], _pipeOverrides: [], _providers: [], _declarations: [], _imports: [], _schemas: [], _activeFixtures: [], _testEnvAotSummaries: function () { ... }, _aotSummaries: [], _templateOverrides: [], _isRoot: true, _rootProviderOverrides: [], platform: PlatformRef{_injector: StaticInjector{parent: ..., source: ..., _records: ...}, _modules: [], _destroyListeners: [], _destroyed: false}, ngModule: function BrowserDynamicTestingModule() { ... }}
我可以从这个输出中看到的最大区别是 _compiler,它分别为 null 和实例化。
此 TestBed "snapshot" 是在 createComponent 调用之前拍摄的
我实际上发现问题可以在这里找到https://github.com/angular/angular/issues/12409
基本上,总结一下,angular实际上是重新定义了 beforeEach 函数,添加了一些非常自以为是的行为。具体来说,它写在这里:https://github.com/angular/angular/blob/master/packages/core/testing/src/before_each.ts#L24
因此,在 beforeAll 中配置的测试模块实际上在 beforeEach 中重置(在 createComponent 调用之前)。
相反,仅使用 beforeEach 实际上有效,因为该模块是在重置调用之后配置的。
所以,要么你按预期使用 beforeEach,牺牲速度(angular 意见是提供的服务在每次测试时应该是新鲜的,这对无状态的东西没有意义)或者你可以选择 2 个不同的解决方案:
1) 再次重新定义全局 beforeEach,将其恢复为默认值,然后放回 angular 行为:这当然是一个 hack 并且非常不稳定并且会抛出随机错误,因为 angular 假设模块是新鲜的自己的内部结构
2) 根本不要使用 beforeEach,在 beforeAll 中配置模块,然后将所有 beforeEach 活动要求为您在每个 it 语句上调用的自定义函数
我正在为我的应用程序编写一些单元测试,我遇到了一个非常奇怪的情况。 我构建了一个 angular 应用程序,我正在使用 karma&jasmine 进行 unit/integration 测试。
这是交易: 我写了这段代码
describe("Body Container component", () => {
let component: BodyContainerComponent;
let fixture: ComponentFixture<BodyContainerComponent>;
beforeEach(async () => {
getTestBed().configureTestingModule({
imports: [AppTestingModule]
});
await getTestBed().compileComponents();
fixture = getTestBed().createComponent(BodyContainerComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
它工作正常,测试被正确编译和执行(并通过)。 不过他们确实需要一段时间,我猜是因为每次都重新配置 testingModule 并编译组件。
所以 a 尝试以这种方式更改它:
beforeAll(async () => {
getTestBed().resetTestingModule();
getTestBed().configureTestingModule({
imports: [AppTestingModule]
});
await getTestBed().compileComponents();
});
beforeEach(async () => {
fixture = getTestBed().createComponent(BodyContainerComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
虽然在 getTestBed().createComponent 上调用失败,但此版本的 logic/API 调用失败,失败原因为:
Error: Illegal state: Could not load the summary for directive BodyContainerComponent.
error properties: Object({ ngSyntaxError: true })
at syntaxError (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:2430:1)
at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getDirectiveSummary (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:18535:1)
at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler.getComponentFactory (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:25979:1)
at CompilerImpl.push../node_modules/@angular/platform-browser-dynamic/fesm5/platform-browser-dynamic.js.CompilerImpl.getComponentFactory (http://localhost:9876/node_modules/@angular/platform-browser-dynamic/fesm5/platform-browser-dynamic.js?:162:1)
at TestingCompilerImpl.push../node_modules/@angular/platform-browser-dynamic/fesm5/testing.js.TestingCompilerImpl.getComponentFactory (http://localhost:9876/node_modules/@angular/platform-browser-dynamic/fesm5/testing.js?:110:1)
at TestBedViewEngine.push../node_modules/@angular/core/fesm5/testing.js.TestBedViewEngine.createComponent (http://localhost:9876/node_modules/@angular/core/fesm5/testing.js?:1905:1)
at Object.<anonymous> (http://localhost:9876/src/test/integration/app/components/body-container.integration-spec.ts?:21:32)
at step (http://localhost:9876/node_modules/tslib/tslib.es6.js?:97:1)
at Object.next (http://localhost:9876/node_modules/tslib/tslib.es6.js?:78:45)
at http://localhost:9876/node_modules/tslib/tslib.es6.js?:71:1
我尝试了各种迭代和更改以使其工作,但错误仍然存在。 经过一些分析,我可以说: 1- async/await 工作正常,只有在 beforeAll 完成后才调用 beforeach 2-每一个测试都给出上面的错误,即使是第一个(这很奇怪,因为逻辑上第一个不应该在两个版本之间改变一英寸
我注意到的一件事是第一个版本测试是随机执行的,而第二个版本似乎是按顺序执行的。 第二个版本的 beforach 上的异步没有改变任何东西,我也尝试调用各种 reset/destroy 方法但 none 似乎有所贡献。
你知道为什么会这样吗? 解决不了也没关系,纵观整个构建过程,毕竟是一个小优化,我比较好奇为什么会这样。
编辑:经过额外分析,通过查看 testBed 实例,我注意到 "beforeEach only" 实现具有
TestBedViewEngine{_instantiated: false, _compiler: TestingCompilerImpl{_compiler: CompilerImpl{_metadataResolver: ..., _delegate: ..., injector: ...}, _directiveResolver: MockDirectiveResolver{_reflector: ..., _directives: ...}, _pipeResolver: MockPipeResolver{_reflector: ..., _pipes: ...}, _moduleResolver: MockNgModuleResolver{_reflector: ..., _ngModules: ...}, _overrider: MetadataOverrider{_references: ...}}, _moduleRef: null, _moduleFactory: NgModuleFactory_{moduleType: function DynamicTestModule() { ... }, _bootstrapComponents: [], _ngModuleDefFactory: function(_l) { ... }}, _compilerOptions: [], _moduleOverrides: [], _componentOverrides: [], _directiveOverrides: [], _pipeOverrides: [], _providers: [], _declarations: [], _imports: [function AppTestingModule() { ... }], _schemas: [], _activeFixtures: [], _testEnvAotSummaries: function () { ... }, _aotSummaries: [], _templateOverrides: [], _isRoot: true, _rootProviderOverrides: [], platform: PlatformRef{_injector: StaticInjector{parent: ..., source: ..., _records: ...}, _modules: [], _destroyListeners: [], _destroyed: false}, ngModule: function BrowserDynamicTestingModule() { ... }}
而 beforeAll 有:
TestBedViewEngine{_instantiated: false, _compiler: null, _moduleRef: null, _moduleFactory: null, _compilerOptions: [], _moduleOverrides: [], _componentOverrides: [], _directiveOverrides: [], _pipeOverrides: [], _providers: [], _declarations: [], _imports: [], _schemas: [], _activeFixtures: [], _testEnvAotSummaries: function () { ... }, _aotSummaries: [], _templateOverrides: [], _isRoot: true, _rootProviderOverrides: [], platform: PlatformRef{_injector: StaticInjector{parent: ..., source: ..., _records: ...}, _modules: [], _destroyListeners: [], _destroyed: false}, ngModule: function BrowserDynamicTestingModule() { ... }}
我可以从这个输出中看到的最大区别是 _compiler,它分别为 null 和实例化。 此 TestBed "snapshot" 是在 createComponent 调用之前拍摄的
我实际上发现问题可以在这里找到https://github.com/angular/angular/issues/12409
基本上,总结一下,angular实际上是重新定义了 beforeEach 函数,添加了一些非常自以为是的行为。具体来说,它写在这里:https://github.com/angular/angular/blob/master/packages/core/testing/src/before_each.ts#L24
因此,在 beforeAll 中配置的测试模块实际上在 beforeEach 中重置(在 createComponent 调用之前)。 相反,仅使用 beforeEach 实际上有效,因为该模块是在重置调用之后配置的。
所以,要么你按预期使用 beforeEach,牺牲速度(angular 意见是提供的服务在每次测试时应该是新鲜的,这对无状态的东西没有意义)或者你可以选择 2 个不同的解决方案: 1) 再次重新定义全局 beforeEach,将其恢复为默认值,然后放回 angular 行为:这当然是一个 hack 并且非常不稳定并且会抛出随机错误,因为 angular 假设模块是新鲜的自己的内部结构 2) 根本不要使用 beforeEach,在 beforeAll 中配置模块,然后将所有 beforeEach 活动要求为您在每个 it 语句上调用的自定义函数