繁体   English   中英

Angular 单元测试子组件

[英]Angular unit test child component

我正在为我的一个由多个组件组成的 Angular 应用程序编写单元测试。 在我的一个组件(组件 A)中,它有一个如下所示的子组件

组分 A

<div class="row row-top">
  <div class="col-12">
    <app-data-grid
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColumnDefs"
      [overlayNoRowsTemplate]="overlayNoRowsTemplate"
      [hasFloatingFilter]="hasFloatingFilter"
      [frameworkComponents]="frameworkComponents"
      [rowData]="rowData"
      [hasMultipleRows]="rowSelection"
      [hasRowAnimation]="hasRowAnimation"
      [multiSortKey]="multiSortKey"
      (rowDataChanged)="onRowDataChanged()"
      (selectionChanged)="onSelectionChanged()"
      (rowClicked)="gotoDetailView($event)"
      (sortChanged)="onSortChanged($event)"
      (columnResized)="onColumnResized()"
      (gridReady)="OnGridReady($event)"
    >
    </app-data-grid>

    <div class="float-right">
      <button
        id="addDevice"
        type="button"
        class="btn btn-brand btn-primary position-relative add-button"
        (click)="gotoAddDevice()"
      >
        <i class="fa fa-plus"></i>
      </button>
    </div>
  </div>
</div>



如果你在 ComponentA 中看到上面的 HTML,我有当列在 Grid 中调整大小时调用函数的组件,它将调用我的 ComponentA 中的 onColumnResized(),如下所示

onColumnResized() {
    const updatedColumns: ColumnWidth[] = [];
    this.columnApi.getColumnState().forEach((column) => {
      updatedColumns.push({ field: column.colId, width: column.width });
    });
    this.store.dispatch(new DevicesActions.UpdateColumnWidth(updatedColumns));
  }

我的问题是我如何监视onColumnResized()或者我可以像下面一样直接调用该函数


    describe('ColumnResized', () => {
        it('call the onColumnResized', () => {
          expect(component.onColumnResized()).toHaveBeenCalled();
        });
      });

我想知道这是否是正确的方法以及我可以使用 spyOn() 而不是直接调用函数

您不应该测试该方法是否被调用。 您应该测试在调整子组件大小时组件的行为是否正确。

有一个很好的库来模拟你的组件: https : //www.npmjs.com/package/ng-mocks

类似的东西:

let columnApiMock: jasmine.SpyObj<ColumnApi>;

beforeEach(() => {
  TestBed.configureTestingModule({
    ...
    providers: [
      { provide: YourStoreService, 
        useValue: jasmine.createSpyObj('YourStoreService', ['dispatch']) },
      { provide: ColumnApi, 
        useValue: jasmine.createSpyObj('ColumnApi', ['getColumnState']) },
    ],
    declarations: [
      AppComponentA, // not mocked
      MockComponent(AppDataGrid),
    ],
  });
  columnApiMock = TestBed.inject(ColumnApi) as jasmine.SpyObj<ColumnApi>;
})

...

it('test if columns are stored on resize', () => {
  // expect you have mocked your colum api, prepare columns that should be returned
  columnApiMock.getColumnState.and.returnValue([...]);
    
  const dataGridMock = ngMocks.find(fixture.debugElement, AppDataGrid);
  // trigger Output from DataGrid
  dataGridMock.columnResized.emit();

  expect(TestBed.inject(YourStoreService).dispatch))
    .toHaveBeenCalledWith(...);
});

这样您的测试就不会依赖于组件的内部实现。

您需要知道是否调用了名为onColumnResized的方法。 您必须确保在调整大小时使用正确的列更新您的商店......

Nidhin,在我提供一些答案之前,让我解释一下对组件进行单元测试的意图。

组件的单元测试意味着您应该检查组件的行为(包括 HTML 和ts代码)。 应该记住的一个重要因素是“我们应该尽可能地将组件(正在测试的)与外部依赖项(主要是服务)隔离开来”

恕我直言,您应该只在onColumnResized()时检查行为是否有效。 您不应该担心app-data-grid是否在(columnResized)上调用它。 app-data-grid测试中,您应该测试是否按照预期引发/调用columnResized

注意:作为集成测试的一部分,您应该测试两个组件一起工作的行为,这意味着您一起测试多个组件的工作(集成

在您的情况下,对于以下代码:

onColumnResized() {
    const updatedColumns: ColumnWidth[] = [];
    this.columnApi.getColumnState().forEach((column) => {
      updatedColumns.push({ field: column.colId, width: column.width });
    });
    this.store.dispatch(new DevicesActions.UpdateColumnWidth(updatedColumns));
 }

你应该做以下测试:

  1. constructor内部的store设为 public(以便我们可以通过在组件外部访问它来监视它)
export class MockColumnApi {
  getColumnState() {
    return [
      {colId: 1, column: 2 }
    ];
  }
}

describe('SomeComponent', () => {
  let component: SomeComponent;
  let fixture: ComponentFixture<SomeComponent>;
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
         // whatever you need to import
      ],
      declarations: [SomeComponent, GridComponent],
      providers: [
        { provide: ColumnApi, useClass: MockColumnApi },
      ],
    }).compileComponents();
  }));

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should create dispatch columns on resize call', () => {
    spyOn(component.columnApi, 'getColumnState').and.callThrough();
    spyOn(component.store, 'dispatch').and.callThrough();
    component.onColumnResized();
    expect(component.columnApi.getColumnState).toHaveBeenCalled()
    expect(component.store.dispatch).toHaveBeenCalledWith(
      new DevicesActions.UpdateColumnWidth({colId: 1, column: 2 })
    );
  });
});

或者您可以简单地覆盖返回类型而不创建MockColumnApi

describe('SomeComponent', () => {
  let component: SomeComponent;
  let fixture: ComponentFixture<SomeComponent>;
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
         // whatever you need to import
      ],
      declarations: [SomeComponent, GridComponent],
      providers: [
        { provide: ColumnApi, useClass: MockColumnApi },
      ],
    }).compileComponents();
  }));

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should create dispatch columns on resize call', () => {
    const sampleData = [
      {colId: 1, column: 2 }
    ];
    spyOn(component.columnApi, 'getColumnState').and.returnValue(sampleData );
    spyOn(component.store, 'dispatch').and.callThrough();
    component.onColumnResized();
    expect(component.columnApi.getColumnState).toHaveBeenCalled()
    expect(component.store.dispatch).toHaveBeenCalledWith(
      new DevicesActions.UpdateColumnWidth(sampleData )
    );
  });
});

您可以参考我的这篇文章,其中我结合了几个主题作为 Angular 单元测试教程的一部分。 我希望它有帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM