Testing Kendo Grid in Angular - javascript

I'm trying to get my Karma tests working with the kendo grid in a brand new Angular project. The specs for this component look like
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { RateTableProviderService } from "../_services/rate-table-provider.service";
import { RateTableComponent } from './rate-table.component';
import { Observable } from 'rxjs/Rx';
import { Response, ResponseOptions } from "#angular/http";
import { IRateTableListViewModel } from "./models/IRateTableListViewModel";
import { GridModule } from '#progress/kendo-angular-grid';
describe('RateTableComponent', () => {
let component: RateTableComponent;
let fixture: ComponentFixture<RateTableComponent>;
let observableSource = [
{
id: "3482cd2f-16f8-4d62-8d5d-d761de35e737",
name: "Rate table 1",
enabled: false,
effectiveDate: new Date(),
creditTierSetName: "",
loanProducts: ""
},
{
id: "3482cd2f-16f8-4d62-8d5d-d761de35e739",
name: "Another rate table",
enabled: false,
effectiveDate: new Date(),
creditTierSetName: "",
loanProducts: ""
}
];
class MockRateTableProviderService extends RateTableProviderService {
constructor() {
super(null);
}
getRateTableData(): Observable<Array<IRateTableListViewModel>> {
return Observable.from([observableSource]);
}
}
beforeEach(async(() => {
let mockRateTableProviderService = new MockRateTableProviderService();
TestBed.configureTestingModule({
declarations: [RateTableComponent],
providers: [
{ provide: RateTableProviderService, useValue: mockRateTableProviderService }
],
imports: [GridModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RateTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
The tests run fine against chrome but in PhantomJS the tests hang.
PhantomJS 2.1.1 (Windows 8 0.0.0) RateTableComponent should be created FAILED
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Everything works fine when running the tests against Chrome. I suspect that I'm not managing the asynchronous import when setting up the test bed correctly. If I pull out the kendo completely then the tests complete. I tried passing in a done call as part of the beforeEach but that also didn't work
beforeEach(async((done) => {
let mockRateTableProviderService = new MockRateTableProviderService();
TestBed.configureTestingModule({
declarations: [RateTableComponent],
providers: [
{ provide: RateTableProviderService, useValue: mockRateTableProviderService }
],
imports: [GridModule]
})
.compileComponents().then(done);
}));

Have you tried defining your fixture and component in the resolve of .compileComponents() as this will then be executed async as well as the setup of your testbed?
`.compileComponents.()
.then(() => {
fixture = TestBed.createComponent(RateTableComponent);
component = fixture.componentInstance;
});`

Try adding a second before each like so:
beforeEach(async(() => {
let mockRateTableProviderService = new MockRateTableProviderService();
TestBed.configureTestingModule({
declarations: [RateTableComponent],
providers: [
{ provide: RateTableProviderService, useValue: mockRateTableProviderService }
],
imports: [GridModule]
})
.compileComponents().then(done);
}));
beforeEach(() => {
done();
}));
not as elegant as my main man simons answer above but should do the trick

Related

Getting undefined on service called from html

I'm currently working on an Angular project and I am creating unit testing for a component using Karma + Jasmine, so I have HTML that has a ngIf calling the API Service as:
HTML
<div class="row" *ngIf="apiService.utilsService.userBelongsTo('operations')"></div">
TS
export class CashFlowSalariesComponent implements OnInit, OnChanges {
constructor(
public apiService: ApiService,
) {}
SPECT.TS
describe('CashFlowSalariesComponent', () => {
let fixture: ComponentFixture < CashFlowSalariesComponent > ;
let mockCashFlowService;
let data;
beforeEach(async(() => {
data = [{
id: 1006,
role: "Developer",
...
}]
mockCashFlowService = jasmine.createSpyObj(['createTableData'])
TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [
RouterTestingModule,
FormsModule,
ReactiveFormsModule,
BrowserModule,
HttpClientTestingModule,
ToastrModule.forRoot({
positionClass: 'toast-bottom-right'
})
],
declarations: [
CashFlowSalariesComponent,
],
providers: [{
provide: ApiService,
useValue: mockCashFlowService
}, UserService, ProfileService, VettingStatusService, ApplicationRoleService,
SeniorityLevelService, PlacementStatusService, EducationLevelService, UtilsService, ShirtSizeService,
CountryService, CityService, PostalCodeService, StateService, ClientSectorService, JobService, ProfileActivityService, ProfileSalaryActivityService, ClientService, RequestTimeOffService, TimeOffTypeService, PulsecheckDetailService, PulsecheckMasterService,
PulsecheckQuestionService, ExpenseService, DepartmentService, ExchangeRateService, SkillCategoriesService, ProfileRoleService,
ToastrService
]
})
fixture = TestBed.createComponent(CashFlowSalariesComponent);
}));
it('should create', () => {
expect(CashFlowSalariesComponent).toBeTruthy();
});
it('should set salaries data correctly', () => {
mockCashFlowService.userBelongsTo = 'operations'
mockCashFlowService.createTableData.and.returnValue( of (data))
debugger;
fixture.detectChanges();
expect(fixture.componentInstance.dataHeaders.length).toBe(10);
})
As you see, I tried to set userBelongsTo as: mockCashFlowService.userBelongsTo = 'operations' but I get an error:
TypeError: Cannot set properties of undefined (setting
'userBelongsTo')
ApiService
#Injectable()
export class ApiService {
public utilsService: UtilsService;
constructor(private injector: Injector) {
this.utilsService = injector.get(UtilsService);
}
}
Utils.Service:
userBelongsTo(groupName: string) {
return this.groups.split(',').reduce((c, g) => c || g.toUpperCase() == groupName.toUpperCase(), false);
}
It is undefined because you have not defined it. You need to add it to your mock and then have it return something.
mockCashFlowService = jasmine.createSpyObj(['createTableData', 'userBelongsTo']);
https://volaresoftware.com/en/technical-posts/mocking-calls-with-jasmine

TypeError karma test : subscribe is not a function

I have a karma unit test and the test fails with below error.
this.gridApi.getScaleWidth().subscribe is not a function
GridApi.ts
export class GridApi {
private scaleWidthSubject = new BehaviorSubject<{value: number}>({value: 0});
public getScaleWidth(): Observable<{value:number}> {
return this.scaleWidthSubject;
}
}
GridComponent.ts
export class GridComponent implements OnInit, OnDestroy, AfterViewInit {
private subscribeToValueChanges() {
this.scaleWidth$ = this.gridApi.getScaleWidth().subscribe( width => {
this.scaleWidth = width.value;
});
}
}
Component.spec.ts
describe('GridComponent', () => {
beforeEach(async () => {
const mockGridApiService = jasmine.createSpyObj("GridApi", {
getScaleWidth () : Observable<{value: number}> {
let scaleWidthSubject = new BehaviorSubject<{value: number}>({value: 0});
return scaleWidthSubject.asObservable();
}
});
}
await TestBed.configureTestingModule({
providers: [ { provide: GridApi, useValue: mockGridApiService} ],
imports: [
HttpClientModule
],
declarations: [ GridComponent ]
})
}
What should the mock getScaleWidth() return to pass the test. Not sure what I'm missing here.
describe('GridComponent', () => {
const mockGridService = jasmine.createSpyObj<GridApi>('GridApi', ['getScaleWidth'])
beforeEach(() => {
mockGridService.getScaleWidth.and.returnValue(of({ value: 0 }));
});
await TestBed.configureTestingModule({
providers: [ { provide: GridApi, useValue: mockGridService} ],
imports: [HttpClientModule],
declarations: [ GridComponent ]
})
it('should call getScaleWidth from service', () => {
// the component function that triggers the service call is private
// make the call from component
expect(mockGridService.getScaleWidth).toHaveBeenCalled();
mockGridService.getScaleWidth().subscribe(response => {
expect(response.value === 0)
})
})
}

Failed: Unexpected value 'TreeviewConfig' imported by the module 'DynamicTestModule'. Please add a #NgModule annotation in unit test

I am trying to test a component containing ngx-treeview. I wrote the following test, but it fails with the error message contained in the title (obviously except the in unit test part):
import { ComponentFixture, TestBed, waitForAsync } from '#angular/core/testing';
import { TreeviewConfig, TreeviewItem, TreeviewModule } from 'ngx-treeview';
import { TabTreeviewComponent } from './tab-treeview.component';
describe('TabTreeviewComponent', () => {
let component: TabTreeviewComponent;
let fixture: ComponentFixture<TabTreeviewComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ TabTreeviewComponent ],
imports: [ TreeviewConfig, TreeviewItem, TreeviewModule.forRoot() ],
providers: [ ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TabTreeviewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
const config = TreeviewConfig.create({​​
hasAllCheckBox: false,
hasFilter: true,
hasCollapseExpand: true,
maxHeight: 435
}​​);
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I already tried initializing the TreeviewConfig as a declaration without any avail apparently. I also tried providing it as a provider - that also did not help at all... I am unsure what else could I try at this point, TestBed must have a way to include the config for the TreeView somehow or am I making a logical fallacy here?
The error is being thrown because you are including non modules on the inports array
Basically you only need to import TreeviewModule
imports: [ TreeviewModule.forRoot() ],

How to write test case for a method addProduct in angular

I want to write test case for a method addItem may be in product.component.spec.ts file . This is my code
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
constructor() { }
productName:any
quantity:any
public data:any[]=[
{
"ID": 101,
"Product": "Moto G",
"Quantity": 2,
},
{
"ID": 102,
"Product": "TV",
"Quantity": 4,
},
{
"ID": 105,
"Product": "Watch",
"Quantity": 2,
},
]
ngOnInit(): void {
}
addItem() {
this.data.push({ Product: this.productName, Quantity: this.quantity});
}
}
I started writing in product.component.spec.ts file
describe('ProductComponent', () => {
let component: ProductComponent;
let fixture: ComponentFixture<ProductComponent>;
fixture = TestBed.createComponent(ProductComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});
product.component.spec.ts file
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { AppComponent } from '../app.component';
import { ProductComponent } from './product.component';
describe('ProductComponent', () => {
let component: ProductComponent;
let fixture: ComponentFixture<ProductComponent>;
TestBed.configureTestingModule({
declarations: [
AppComponent,
ProductComponent
]
});
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ProductComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProductComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
here is how you should do it.
describe('ProductComponent', () => {
let component: ProductComponent;
let fixture: ComponentFixture<ProductComponent>;
TestBed.configureTestingModule({
declarations: [
AppComponent,
ProductComponent
]
});
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ProductComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProductComponent);
component = fixture.componentInstance;
component.data = [{productName : "p1", quantity : 3}]
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should add items to "data"', () => {
expect(component.data.length).toBe(1); // since you have initialized the variable
component.productName = "Prod1";
component.quantity = 1;
component.addItem(); // this will trigger the method
expect(component.data.length).toBe(4); // this will show that the entry was added in "this.data"
});
});
I would also suggest you to go through this intro article of mine to understand unit testing in angular. There are few links attached to this article as well , which can help you understand the best practices of testing a component. Cheers :)
A testcase always includes 4-5 steps:
Tear up
Data preperation
Execution
Verification
(optional) Tear down
What you already have is the first step. For the second step you need to prepare the required fields, which are data, quantity and productName.
component.productName = 'Awesome Product';
component.quantity = 3;
component.data = [];
The third steps only executes the method you want to test.
component.addItem();
With the fourth step you check the result, which is now the extended array.
expect(component.data).toEqual([{ Product: 'Awesome Product', Quantity: 3 }]);

Angular 4 unit test, but getting error No provider for Http

I'm trying to learn how to run a unit test component on angular 4, but I'm not getting success, when I run the test with the code below I get this error:
Error: No provider for http! and Failed: :
could not find an object to spy upon for filexGeneralData()
I don't know if I'm on the right way...
Take a look at my code
my spec file
import { TestBed, async, inject } from '#angular/core/testing';
import { HttpModule } from '#angular/http';
import { of } from 'rxjs/observable/of';
import { filex } from '../../../models/filex';
import { filexService } from '../../../services/filex.service';
import { fileyfilexComponent } from './filey-filex.component';
import { dataService } from '../../../services/data.service';
describe('fileyfilexComponent', () => {
let filexService;
let myComponent;
let fixture;
let element;
beforeEach(
async(() => {
TestBed.configureTestingModule({
declarations: [fileyfilexComponent],
providers: [filexService, dataService],
imports: [HttpModule]
}).compileComponents();
})
);
beforeEach(inject([filexService], s => {
filexService = s;
fixture = TestBed.createComponent(fileyfilexComponent);
myComponent = fixture.componentInstance;
element = fixture.nativeElement;
}));
it(
'should call getUsers and return list of users',
async(() => {
const response: filex[] = [];
spyOn(filexService, 'filexGeneralData').and.returnValue(of(response));
myComponent.method1();
fixture.detectChanges();
expect(myComponent.datafilex).toEqual(response);
})
);
});
You just need to include HubWrapperComponent in your TestBed. In the providers array, you need to include all of the services provided to your component being tested (better yet, you should provide "mocked" versions of those service). So, you could get the error to "go away" by simply adding HubWrapperComponent to the providers array in your spec file's TestBed.configureTestingModule method. It will end up looking like this:
spec.ts:
TestBed.configureTestingModule({
declarations: [IndicatorsDashboardComponent],
providers: [DashboardService, DadosService, HubWrapperComponent],
imports: [HttpModule]
}).compileComponents();
An additional piece of advice: I would advise using jasmine to mock your HubWrapperComponent (which seems to be a wrapper over the HttpClient?).
mockWrapper = jasmine.createSpyObj('http', ['get']);
Then in your providers array:
{provide: HubWrapperComponent, useValue: mockWrapper}
That approach would look something like this:
let mockHub: SpyObj<HubWrapperComponent>;
beforeEach(
async(() => {
mockHub = jasmine.createSpyObj('http', ['get']);
TestBed.configureTestingModule({
declarations: [IndicatorsDashboardComponent],
providers: [
DashboardService,
DadosService,
{ provide: HubWrapperComponent, useValue: mockHub }
],
imports: [HttpModule]
}).compileComponents();
})
);
Mocking a service / anything that makes Http calls is preferred because you don't want to make real requests in your tests.

Categories