我正在使用 Jasmine/Karma 运行 Angular 单元测试。目前收到以下错误:
TypeError: Cannot read property 'name' of undefined
at ReplicateListComponent_Template (ng:///ReplicateListComponent.js:221:64)
at executeTemplate (http://localhost:9876/_karma_webpack_/vendor.js:49857:9)
at refreshView (http://localhost:9876/_karma_webpack_/vendor.js:49723:13)
at refreshComponent (http://localhost:9876/_karma_webpack_/vendor.js:50894:13)
at refreshChildComponents (http://localhost:9876/_karma_webpack_/vendor.js:49520:9)
ReplicateListComponent.js 的第 221 行:
jit___property_12('title',jit___pipeBind1_9(13,28,'upload_replicate_tooltip'))('routerLink',
jit___pureFunction1_10(32,_c1,ctx.managementConfig.name));
jit___advance_6(3);
ctx.managementConfig.name
- ctx 没有命名的变量managementConfig
- 见截图。
单元测试代码:
let component: ReplicateViewComponent;
let fixture: ComponentFixture<ReplicateViewComponent>;
let params: Subject<Params>;
let queryParams: Subject<Params>;
let mockManagementService: jasmine.SpyObj<ManagementConfigService>;
let getManagementConfig: Subject<IManagementConfig>;
let environmentSwitched: Subject<string>;
beforeEach(async () => {
params = new Subject<Params>();
queryParams = new Subject<Params>();
mockManagementService = jasmine.createSpyObj<ManagementConfigService>([ 'getManagementConfig', 'getCurrentManagementConfig', 'findManagementConfig', 'environmentSwitched' ]);
getManagementConfig = new Subject<IManagementConfig>();
environmentSwitched = new Subject<string>();
...
await TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
ToastrModule.forRoot({}),
RouterTestingModule.withRoutes([
{ path: ':env/replicates', component: DummyComponent },
]),
...
],
declarations: [
ReplicateViewComponent,
...
ReplicateListComponent,
...
],
providers: [
{ provide: ManagementConfigService, useValue: mockManagementService },
{ provide: ActivatedRoute, useValue: ACTIVATED_ROUTE },
{ provide: RuntimeConfig, useValue: RUNTIME_CONFIG },
...
],
})
.compileComponents();
mockManagementService.getManagementConfig.and.returnValue(getManagementConfig);
mockManagementService.getCurrentManagementConfig.and.returnValue(RUNTIME_CONFIG.environments[0]);
mockManagementService.findManagementConfig.and.returnValue(RUNTIME_CONFIG.environments[0]);
mockManagementService.environmentSwitched.and.returnValue(environmentSwitched);
});
beforeEach(() => {
fixture = TestBed.createComponent(ReplicateViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
component.list.managementConfig = RUNTIME_CONFIG.environments[0];
component.list.replicates = REPLICATIONS;
component.details.replicate = REPLICATIONS[0];
const router = TestBed.inject(Router);
router.initialNavigation();
getManagementConfig.next(RUNTIME_CONFIG.environments[0]);
environmentSwitched.next(RUNTIME_CONFIG.environments[0].name);
params.next({ id: '11111111-1111-1111-1111-111111111111' });
});
it('should create', () => {
expect(component).toBeTruthy();
expect(component.list).toBeTruthy();
expect(component.details).toBeTruthy();
});
我期望有一个名为 的变量的原因managementConfig
:
export class ReplicateListComponent extends BaseContainerComponent implements OnInit {
managementConfig: IManagementConfig;
...
ngOnInit(): void {
this.subs.add(
this.route.params.subscribe(
(params: Params) => {
this.managementConfig = this.managementService.findManagementConfig(params.env);
ReplicateListComponent 的 HTML 的违规部分 - 请参阅 routerLink
<div class="my-auto ml-auto">
<a data-cy="batchReplicateLink" [title]="'upload_replicate_tooltip' | translate" [routerLink]="[ '/', managementConfig.name, 'replicates', 'batch']" queryParamsHandling="preserve">
<i class="icons8-sm icons8-Copy"></i>{{ 'replicate_batch_link' | translate }}
</a>
</div>
ReplicateListComponent.js 与没有错误的 AssetListComponent.js 非常相似。从截图中可以看出,AssetListComponent 的上下文中有一个 managementConfig:
AssetListComponent 的 routerLink HTML 本质上是一样的:
<a
data-cy="uploadLink"
[title]="'upload_asset_tooltip' | translate"
[routerLink]="[ '/', managementConfig.name, 'assets', 'upload']"
[queryParamsHandling]="'preserve'"
*ngIf="managementConfig?.upload"
>
<i class="icons8-sm icons8-Upload"></i>{{ 'asset_list_upload_link' | translate }}
</a>
Backend code of AssetListComponent:
export class AssetListComponent extends BaseContainerComponent implements OnInit {
managementConfig: IManagementConfig;
...
ngOnInit(): void {
this.subs.add(
this.route.params.subscribe(
(params: Params) => {
this.managementConfig = this.managementService.findManagementConfig(params.env);
Thanks.
1.) The error is caused because the first fixture.detectChanges()
you call, managementConfig
is undefined at that point in time. Your other component, there is an *ngIf
where this issue doesn't happen where it will be explained in #3.
2.) The fix would be to hide this HTML until managementConfig
is truthy:
<div class="my-auto ml-auto" *ngIf="managementConfig">
<a data-cy="batchReplicateLink" [title]="'upload_replicate_tooltip' | translate" [routerLink]="[ '/', managementConfig.name, 'replicates', 'batch']" queryParamsHandling="preserve">
<i class="icons8-sm icons8-Copy"></i>{{ 'replicate_batch_link' | translate }}
</a>
</div>
3.) The error is in one place but not the other because in the second one you have an *ngIf
:
The *ngIf="managementConfig?.upload"
first checks if managementConfig
exists (with the question mark) and if it does, does upload
exist in the object, if it does, go ahead and show this HTML mark up.
如果您删除 this *ngIf
,我认为这里也会发生错误。
<a
data-cy="uploadLink"
[title]="'upload_asset_tooltip' | translate"
[routerLink]="[ '/', managementConfig.name, 'assets', 'upload']"
[queryParamsHandling]="'preserve'"
*ngIf="managementConfig?.upload"
>
<i class="icons8-sm icons8-Upload"></i>{{ 'asset_list_upload_link' | translate }}
</a>
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句