无法在单元测试中设置未定义的属性“componentInstance”

问题描述 投票:0回答:1

我尝试了不同的方法(通过构造函数注入组件,以不同的方式创建模拟和间谍,将这个间谍注入元素,...),但最后我总是收到同样的错误:无法设置未定义的属性“componentInstance”。你能给我任何想法吗?

组件

openUpdate(rowSelected: iData) {
        let dialogRef = this.dialog.open(DialogEditComponent, {});
        dialogRef.componentInstance.dialogRef = dialogRef;
        dialogRef.componentInstance.selectedData = rowSelected;
        const sub = dialogRef.componentInstance.onAdd.subscribe((data: iData) => {
            if (data) {
                this.update(data);
            }
        });
        dialogRef.afterClosed().subscribe(() => {
            sub.unsubscribe();
        });
    }

SPEC.TS

describe('Component1Component', () => {
  let component: Componenet1Component;
  let fixture: ComponentFixture<Componenet1Component>;
  let dataServiceSpy: jasmine.SpyObj<DataService>;
  let toastrServiceSpy: jasmine.SpyObj<ToastrService>;
  let dialogSpy: any;
  let dialogRefSpy: any;

  beforeEach(async () => {
    dialogSpy = {
      open: jasmine.createSpy('open').and.returnValue({
        componentInstance: {
          onAdd: jasmine.createSpyObj('onAdd', ['subscribe'])
        },
        afterClosed: () => {
          return jasmine.createSpyObj('afterClosed', ['subscribe']);
        },
      })
    };
    toastrServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']);

    await TestBed.configureTestingModule({
      declarations: [Component1Component, DialogEditComponent],
      imports: [MatDialogModule],
      providers: [
        { provide: MatDialog, useValue: dialogSpy },
        { provide: MatDialogRef, useValue: dialogRefSpy },
        { provide: FinalistService, useValue: dataServiceSpy },
        { provide: ToastrService, useValue: toastrServiceSpy },
        { provide: MAT_DIALOG_DATA, useValue: {} },
      ],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(CampaignFinalistsComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

it('should open the edit dialog with the selected data', async () => {
    component = new Component1Component(dialogSpy, dataServiceSpy, toastrServiceSpy);

    dialogRefSpy.componentInstance = {
      dialogRef: dialogRefSpy,
      selectedFinalist: mockData[0],
      onAdd: of(mockData[0])
    };

    dialogSpy.open.and.returnValue(dialogRefSpy);

    spyOn(component, 'update');

    component.openUpdate(mockData[0]);

    expect(dialogSpy.open).toHaveBeenCalledWith(DialogEditComponent, {});
    expect(dialogRefSpy.componentInstance.selectedData).toEqual(mockData[0]);

    expect(component.update).toHaveBeenCalledWith(mockData[0]);
    expect(dialogRefSpy.afterClosed).toHaveBeenCalled();
  });

但正如我所说,我不断收到此错误:

TypeError: Cannot set property 'componentInstance' of undefined
            at UserContext.<anonymous> (src/app/features/Component1.component.spec.ts:108:35)
            at Generator.next (<anonymous>)
            at asyncGeneratorStep (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:3:1)
            at _next (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:22:1)
            at executor (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:27:1)
            at new ZoneAwarePromise (node_modules/zone.js/dist/zone.js:1351:25)
            at UserContext.apply (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:19:1)
            at _ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:409:30)
            at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:303:43)
            at _ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:408:56)

非常感谢

angular unit-testing jasmine karma-jasmine angular15
1个回答
0
投票

你正在创建一个 dialogRefSpy 对象,但你没有用任何值初始化它。因此,当您尝试在其上设置 componentInstance 属性时,它仍然是未定义的。

您需要在设置 componentInstance 属性之前初始化 dialogRefSpy 对象。您可以使用 jasmine.createSpyObj 函数创建一个新对象,并在调用 open 之前将其分配给 dialogRefSpy.componentInstance。

SPEC.TS

it('should open the edit dialog with the selected data', async () => {
  component = new Component1Component(dialogSpy, dataServiceSpy, toastrServiceSpy);

  const dialogRefInstanceSpy = jasmine.createSpyObj('dialogRefInstanceSpy', ['afterClosed', 'close']);
  dialogRefInstanceSpy.selectedData = mockData[0];
  dialogRefInstanceSpy.onAdd = of(mockData[0]);

  dialogRefSpy = jasmine.createSpyObj('dialogRefSpy', ['afterClosed']);
  dialogRefSpy.componentInstance = dialogRefInstanceSpy;

  dialogSpy.open.and.returnValue(dialogRefSpy);

  spyOn(component, 'update');

  component.openUpdate(mockData[0]);

  expect(dialogSpy.open).toHaveBeenCalledWith(DialogEditComponent, {});
  expect(dialogRefInstanceSpy.selectedData).toEqual(mockData[0]);

  expect(component.update).toHaveBeenCalledWith(mockData[0]);
  expect(dialogRefInstanceSpy.afterClosed).toHaveBeenCalled();
});

我用 jasmine.createSpyObj 创建了一个新对象 dialogRefInstanceSpy 并将其分配给 dialogRefSpy.componentInstance。然后我们在调用 open 之前在 dialogRefInstanceSpy 上设置 selectedData 和 onAdd 属性。

希望对您有所帮助...

更新SPEC.TS

describe('Component1Component', () => {
  let component: Componenet1Component;
  let fixture: ComponentFixture<Componenet1Component>;
  let dataServiceSpy: jasmine.SpyObj<DataService>;
  let toastrServiceSpy: jasmine.SpyObj<ToastrService>;
  let dialogSpy: any;
  let dialogRefSpy: any;

  beforeEach(async () => {
    dialogSpy = {
      open: jasmine.createSpy('open').and.returnValue({
        componentInstance: {
          onAdd: jasmine.createSpyObj('onAdd', ['subscribe'])
        },
        afterClosed: () => {
          return jasmine.createSpyObj('afterClosed', ['subscribe']);
        },
      })
    };
    toastrServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']);

    await TestBed.configureTestingModule({
      declarations: [Component1Component, DialogEditComponent],
      imports: [MatDialogModule],
      providers: [
        { provide: MatDialog, useValue: dialogSpy },
        { provide: MatDialogRef, useValue: dialogRefSpy },
        { provide: FinalistService, useValue: dataServiceSpy },
        { provide: ToastrService, useValue: toastrServiceSpy },
        { provide: MAT_DIALOG_DATA, useValue: {} },
      ],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(CampaignFinalistsComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

it('should open the edit dialog with the selected data', async () => {
  component = new Component1Component(dialogSpy, dataServiceSpy, toastrServiceSpy);

  const dialogRefInstanceSpy = jasmine.createSpyObj('dialogRefInstanceSpy', ['afterClosed', 'close']);
  dialogRefInstanceSpy.selectedData = mockData[0];
  dialogRefInstanceSpy.onAdd = of(mockData[0]);

  dialogRefSpy = jasmine.createSpyObj('dialogRefSpy', ['afterClosed']);
  dialogRefSpy.componentInstance = dialogRefInstanceSpy;

  dialogSpy.open.and.returnValue(dialogRefSpy);

  spyOn(component, 'update');
  
  const onAddSpy = dialogRefInstanceSpy.componentInstance.onAdd;
  onAddSpy.subscribe.and.returnValue(of(mockData[0]));

  component.openUpdate(mockData[0]);

  expect(dialogSpy.open).toHaveBeenCalledWith(DialogEditComponent, {});
  expect(dialogRefInstanceSpy.selectedData).toEqual(mockData[0]);

  expect(component.update).toHaveBeenCalledWith(mockData[0]);
  expect(dialogRefInstanceSpy.afterClosed).toHaveBeenCalled();
});
© www.soinside.com 2019 - 2024. All rights reserved.