I am writing unit test for one of my Angular application which consists of multiple components. In one of my component (Component A), it has a child component like below
Component 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 thecomponent which calls the functions when the column is resized in the Grid which would call the onColumnResized() inside my ComponentA like below
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
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
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
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. 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.
Unit testing of a component means that you should check the behavior (which includes HTML &
ts
code) of a component. 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. You should not be worried whether app-data-grid
is calling it or not on (columnResized)
. In app-data-grid
tests, you should test whether columnResized
is raised/invoked as per the expectation.
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:
store
inside the constructor
as public (so that we can put spy on it by accessing it outside the component)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
:
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. I hope it helpes.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.