I'm subscribing to a behavior subject in onInit and based on the result I'm calling a function. My code is like
subscription = new Subscription();
constructor(private myService: MyService) {}
ngOnInit() {
this.subscription = this.myService.event.subscribe(response => {
if(response){
this.myFunction();
}
});
}
myFunction() {}
and I'm test this by trying like below
describe('AppComponent', () => {
let event = new BehaviorSubject(false);
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
AppComponent
], imports: [
], providers: [{
provide: MyService, useValue: {
event: event
}
}]
}).compileComponents();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call myFunction', (done) => {
const myService = fixture.debugElement.injector.get(MyService);
myService.event.next(true);
component.ngOnInit();
const spy = spyOn(component, 'myFunction');
myService.event.subscribe((event: boolean) => {
expect(spy).toHaveBeenCalled();
done();
})
});
});
and I'm getting my spy is not called. Please help me to fix my code. Thanks a lot.
You're spying too late it seems.
Try the following:
// !! Spy first
const spy = spyOn(component, 'myFunction');
// !! Then call ngOnInit
component.ngOnInit();
Edit
Try with fakeAsync and tick.
it('should call myFunction, fakeAsync(() => {
const myService = fixture.debugElement.injector.get(MyService);
myService.event.next(true);
const spy = spyOn(component, 'myFunction');
component.ngOnInit();
tick();
expect(spy).toHaveBeenCalled();
}));
The fakeAsync/tick should hopefully wait until the subscribe is done before moving on to the expect.
Related
I am trying to test my router event this is what te code in the TS file look like:
constructor(
private router: Router
) {
router.events.subscribe(route => {
// I removed the code because it doesn`t matter for the solution
});
}
unittest:
describe('MainComponent', () => {
let methodSpy: jasmine.Spy;
const eventSubject = new ReplaySubject<RouterEvent>(1);
const routerMock = {
navigate: jasmine.createSpy('navigateByUrl'),
navigateByUrl: jasmine.createSpy('navigateByUrl'),
events: eventSubject.asObservable(),
url: 'test/url',
createUrlTree: (commands, navExtras = {}) => {},
serializeUrl: (commands, navExtras = {}) => {}
}
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule, FormsModule],
declarations: [],
providers: [
{ provide: Router, useValue: routerMock},
{ provide: ActivatedRoute, useValue: routerMock},
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MainComponent);
component = fixture.componentInstance;
methodSpy = spyOn(component, 'AdminPanelStarted');
//fixture.detectChanges();
});
it('should trigger the router event and hit the function "Panel"', () => {
eventSubject.next(new NavigationEnd(1, 'test', 'routeUrl'));
expect(methodSpy).toHaveBeenCalled();
});
});
this is the error I am getting:
I can`t find the solution. The only thing I want to test is if the correct function have been called after entering the router.events subscription. The observable is triggered but gives the error that startsWith can not be done on undefined. But what is undefined?
Thx a Lot!
I am trying to spy popover present method in my ionic 5 / angular 11 project but getting an error
Unhandled Promise rejection: Cannot read property 'then' of undefined
Here is unit test code
describe('LoginPage', () => {
let popoverSpy = jasmine.createSpyObj('Popover', ['create', 'present', 'onDidDismiss', 'dismiss']);
let popoverCtrlSpy = jasmine.createSpyObj('PopoverController', ['create']);
popoverCtrlSpy.create.and.callFake(function () {
return popoverSpy;
});
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [LoginPage],
imports: [
IonicModule.forRoot(),
TranslateModule.forChild(),
ComponentsModule,
TranslateModule.forRoot({
loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
})
],
providers: [{ provide: PopoverController, useValue: popoverCtrlSpy }]
}).compileComponents()
it("check popover preset", () => {
component.openEntitySelection();
fixture.detectChanges();
expect(popoverSpy.present).toHaveBeenCalled()
})
}
private async openEntitySelection() {
let popover = await this.popoverCtrl.create({
component: PopoverPage
});
await popover.present();
popover.onDidDismiss().then((response) => {
//Handle response
})
}
Thanks in advance!
It seems you're not mocking onDidDismiss to return a promise. You also need to use fixture.whenStable or waitForAsync utilities to wait for the promise to be resolved before doing your assertion.
Try this:
describe('LoginPage', () => {
let popoverSpy = jasmine.createSpyObj('Popover', ['create', 'present', 'onDidDismiss', 'dismiss']);
// !! -- Add this line -- !!
// I am mock resolving it to a value of true, you can resolve it to any value
popOverSpy.onDidDismiss.and.returnValue(Promise.resolve(true));
// !! --- !!
let popoverCtrlSpy = jasmine.createSpyObj('PopoverController', ['create']);
popoverCtrlSpy.create.and.callFake(function () {
return popoverSpy;
});
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [LoginPage],
imports: [
IonicModule.forRoot(),
TranslateModule.forChild(),
ComponentsModule,
TranslateModule.forRoot({
loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
})
],
providers: [{ provide: PopoverController, useValue: popoverCtrlSpy }]
}).compileComponents()
it("check popover preset", (done) => { // add done to let jasmine know you're done
component.openEntitySelection();
fixture.detectChanges();
// you have to wait at least for one fixture.whenStable I am thinking
// because we are returning a promise and we have to ensure that the promises
// have completed before doing our assertion.
fixture.whenStable().then(() => {
expect(popoverSpy.present).toHaveBeenCalled();
done();
});
});
}
private async openEntitySelection() {
let popover = await this.popoverCtrl.create({
component: PopoverPage
});
await popover.present();
popover.onDidDismiss().then((response) => {
//Handle response
})
}
Argument of type 'Promise' is not assignable to parameter of type 'Promise<OverlayEventDetail>'.
Type 'boolean' has no properties in common with type 'OverlayEventDetail'
I am trying to write a unit test of an angular 6 component which is initializing the bootstrap-daterangepicker in the ngAfterViewInit() method. When I run my unit test it gives the following error:
TypeError: $(...).daterangepicker is not a function
this is the code from the actual component(EmployeeComponent):
ngAfterViewInit(): void {
this.initializeDatePicker(this);
}
initializeDatePicker(that: any) {
const start = moment().subtract(7, 'days');
const end = moment();
$('#reportrange').daterangepicker({
startDate: start,
endDate: end,
maxDate: moment(),
ranges: {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')]
}
}, cb);
cb(start, end);
}
this is the code from my test class:
describe('EmployeeComponent', () => {
let component: EmployeeComponent;
let fixture: ComponentFixture<EmployeeComponent>;
let messageService: NotificationService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [EmployeeComponent]
})
.overrideComponent(EmployeeComponent, {
set: {
template: '',
providers: [
{ provide: NotificationService, useValue: messageService },
{ provide: ActivatedRoute, useValue: { queryParams: of({ emp: "123" }) } }
]
}
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EmployeeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
You don't need to handle it in your test cases. That component should be initialized in a separate service and you can simply mock that method from the service. In the way you can avoid this error.
let say you move all the code of the initializeDatePicker() in a method in some service let say common-service.ts and you can simply call that service from this method like
this.commonServiceObj.initializeDatePicker();
Now after doing this, you can simply mock initializeDatePicker() from the service object and error should be gone.
I am trying to test a component which uses Observables and then cascades through several function calls when the Observable resolves. Here is a version of the component.
export class NotificationComponent implements OnInit {
private answerSubscription: Subscription;
constructor(public toasterService: ToasterService, private commentService: CommentService) { }
ngOnInit() {
this.answerSubscription = this.commentService.answer$.subscribe(
answer => this.commentComplete(answer));
}
commentComplete(answer) {
this.toasterService.clear(answer.toastId);
let promptAns = this.search(answer.toastId);
}
}
and here is my test:
class MockToastService {
clear() {}
}
class MockCommentService {
answer$: Observable<any>;
constructor() {
this.answer$ = Observable.of({toastId: '123'});
}
}
describe('NotificationComponent', () => {
let component: NotificationComponent; let fixture: ComponentFixture<NotificationComponent>;
let mockComment = new MockCommentService(); let mockToast = new MockToastService();
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [NotificationComponent, MockToast],
providers: [{ provide: ToasterService, useValue: mockToast },
{ provide: CommentService, useValue: mockComment }]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NotificationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should complete notification on answer', () => {
spyOn(component, 'commentComplete'); spyOn(mockToast, 'clear');
expect(component.commentComplete).not.toHaveBeenCalled();
component.ngOnInit();
expect(component.commentComplete).toHaveBeenCalled();
expect(mockToast.clear).toHaveBeenCalled();
});
});
The test passes on expect(component.commentComplete).toHaveBeenCalled();, but fails on expect(mockToast.clear).toHaveBeenCalled(). As you can see from the component, toasterService.clear( should be called straight after commentComplete, however, I have stepped through with a debugger, and the test criteria is being checked before the clear function is being called.
I have tried adding fakeAsync and tick(), but am still facing the issue. Any idea how I can make this test's timing work?
You should use fake Async here but as understand there the issues was not with it.
You fake 'commentComplete' function by spyOn(component,'commentComplete') but you need to spy and do its job. change to 'spyOn(component, 'commentComplete').and.callThrough();'
Spies: and.callThrough. By chaining the spy with and.callThrough, the spy will still track all calls to it but in addition it will delegate to the actual implementation.
https://jasmine.github.io/2.0/introduction.html
here is the code that should work:
it('should complete notification on answer', fakeAsync(() => {
const spyComplete = spyOn(component, 'commentComplete').and.callThrough();
const spyToast = spyOn(mockToast, 'clear');
expect(component.commentComplete).not.toHaveBeenCalled();
component.ngOnInit();
tick();
expect(spyComplete).toHaveBeenCalled();
expect(spyToast).toHaveBeenCalled();
}));
I am testing a angular component and the code is
ngOnInit(): void {
this.getNar();
}
getNar(): void {
let self = this;
this.dashboardService.getNar().subscribe(
res => self.narIds = res.narIds,
error => self.error = error,
function () {
self.narIds.forEach(element => {
// Some Code
});
}
);
}
The Service provider for this i.e Dashboard Service is
getNar(): Observable<any> {
return this.http.get(Config.Api.GetNar + '1/nar').map((res: Response) => res.json());
}
And my Test cases are:
let res = '"narIds":[{"id":1,"narId":"104034-1","narName":"SDLC Platform"},{"id":2,"narId":"64829-1","narName":"EMS-EMS"}]';
describe('Application Health Component', () => {
beforeEach( async(() => {
TestBed.configureTestingModule({
providers: [MockBackend, DashboardService],
imports: [ChartsModule, SlimScrollModule, HttpModule],
declarations: [CompletedFilterPipe, ApplicationHealthComponent]
})
.compileComponents()
.then(createComponent);
}));
it('should call the getNar when ngOnInit is called', async(() => {
spyOn(dashboardService, 'getNar').and.returnValue(Observable.of(res));
comp.ngOnInit();
expect(dashboardService.getNar).toHaveBeenCalled();
}));
});
function createComponent() {
fixture = TestBed.createComponent(ApplicationHealthComponent);
comp = fixture.componentInstance;
dashboardService = fixture.debugElement.injector.get(DashboardService);
};
The problem I am getting is the test case gives an error that forEach is undefined.
The error message is not that forEach function is not defined, it's that your object "self.narIds" is undefined. Fairly sure this is due to the way you declared your onComplete function in Observable.subscribe
related to this Rx Subscribe OnComplete fires but cannot use the data
change your
function () {
self.narIds.forEach(element => {
// Some Code
});
code to
() => {
self.narIds.forEach(element => {
// Some Code
});