简体   繁体   English

Angular 单元测试子组件

[英]Angular unit test child component

I am writing unit test for one of my Angular application which consists of multiple components.我正在为我的一个由多个组件组成的 Angular 应用程序编写单元测试。 In one of my component (Component A), it has a child component like below在我的一个组件(组件 A)中,它有一个如下所示的子组件

Component 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>



If you see the above HTML in the ComponentA i am having the如果你在 ComponentA 中看到上面的 HTML,我有component which calls the functions when the column is resized in the Grid which would call the onColumnResized() inside my ComponentA like below当列在 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));
  }

My question is how do i spy the onColumnResized() or can i call the function directly like below我的问题是我如何监视onColumnResized()或者我可以像下面一样直接调用该函数


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

I Would like to know whether this is the correct approach as well as can i use spyOn() instead of calling the function directly我想知道这是否是正确的方法以及我可以使用 spyOn() 而不是直接调用函数

You should not test if the method is called.您不应该测试该方法是否被调用。 You should test if the component behaves correct when the child component is resized.您应该测试在调整子组件大小时组件的行为是否正确。

There is a good library to mock your components: https://www.npmjs.com/package/ng-mocks有一个很好的库来模拟你的组件: https : //www.npmjs.com/package/ng-mocks

Something like that:类似的东西:

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(...);
});

This way your tests don't rely on the internal implementation of your component.这样您的测试就不会依赖于组件的内部实现。

You need to know if a method named onColumnResized is called.您需要知道是否调用了名为onColumnResized的方法。 You have to ensure, that your store is updated with the correct columns when a resize happens...您必须确保在调整大小时使用正确的列更新您的商店......

Nidhin, before I provide some answer let me explain the intention of unit testing for a component. Nidhin,在我提供一些答案之前,让我解释一下对组件进行单元测试的意图。

Unit testing of a component means that you should check the behavior (which includes HTML & ts code) of a component.组件的单元测试意味着您应该检查组件的行为(包括 HTML 和ts代码)。 One important factor which should be kept in mind is that "we should isolate the component(which is being tested) from external dependencies (mostly services) as much as possible"应该记住的一个重要因素是“我们应该尽可能地将组件(正在测试的)与外部依赖项(主要是服务)隔离开来”

IMHO, you should only check if the behavior is working when onColumnResized() is called.恕我直言,您应该只在onColumnResized()时检查行为是否有效。 You should not be worried whether app-data-grid is calling it or not on (columnResized) .您不应该担心app-data-grid是否在(columnResized)上调用它。 In app-data-grid tests, you should test whether columnResized is raised/invoked as per the expectation.app-data-grid测试中,您应该测试是否按照预期引发/调用columnResized

Note: You should test the behavior of both components working together as a part of Integration testing , which means that you test the working of several components together ( integrated )注意:作为集成测试的一部分,您应该测试两个组件一起工作的行为,这意味着您一起测试多个组件的工作(集成

In your case, for below code:在您的情况下,对于以下代码:

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

you should do below tests:你应该做以下测试:

  1. Make store inside the constructor as public (so that we can put spy on it by accessing it outside the component)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 })
    );
  });
});

OR you can simply override the return type without creating a MockColumnApi :或者您可以简单地覆盖返回类型而不创建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 )
    );
  });
});

You can refer this article of mine where I have combined few topics as a part of tutorial of Unit testing in Angular.您可以参考我的这篇文章,其中我结合了几个主题作为 Angular 单元测试教程的一部分。 I hope it helpes.我希望它有帮助。

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

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