簡體   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