角单元测试子组件

时间:2020-09-18 18:05:07

标签: angular typescript unit-testing jasmine karma-jasmine

我正在为包含多个组件的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()代替直接调用该函数

2 个答案:

答案 0 :(得分:1)

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

有一个很好的库可以模拟您的组件: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的方法。您必须确保在调整大小时使用正确的列更新商店...

答案 1 :(得分:1)

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

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

恕我直言,您仅应在调用ts时检查行为是否正常。您不必担心onColumnResized()是否在app-data-grid上调用它。在(columnResized)测试中,您应该测试是否app-data-grid是否按照预期进行了募集/调用。

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

对于您而言,以下代码:

columnResized

您应该执行以下测试:

  1. onColumnResized() { const updatedColumns: ColumnWidth[] = []; this.columnApi.getColumnState().forEach((column) => { updatedColumns.push({ field: column.colId, width: column.width }); }); this.store.dispatch(new DevicesActions.UpdateColumnWidth(updatedColumns)); } 内部的store设为公开(这样我们就可以通过在组件外部访问它来监视它)
constructor

或者您可以简单地覆盖返回类型而无需创建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

您可以参考this article of mine,其中我结合了几个主题,作为Angular单元测试教程的一部分。希望对您有所帮助。