Angular8 Unittest Router Events - unit-testing

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!

Related

Angular 12 unit test, spy a function inside subscribe

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.

ionic 5 unit test with popover controller

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'

How to handle bootstrap-daterangepicker in angular component unit test?

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.

How to test router.navigate with subscribe ? Angular2

I'm pretty new in unit testing in Angular 2 so i'm asking your help.
My logout function :
logOut() {
this.authService.logOut().subscribe(() => {
this.router.navigate(['login']);
});
}
And my unit test :
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
let authenticationService: AuthenticationService;
let mockLogOut = {
logOut: () => { }
};
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
HttpModule,
CommonModule,
ReactiveFormsModule,
TranslateModule,
RouterTestingModule.withRoutes([
{ path: 'login', component: LoginComponent }
])
],
declarations: [HomeComponent, LoginComponent],
providers: [
{ provide: AuthenticationService, useValue: mockLogOut },
TranslateService,
TRANSLATION_PROVIDERS
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
authenticationService = TestBed.get(AuthenticationService);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('Authentication Tests', () => {
it('should log out the user', inject([AuthenticationService], (mockLogin: AuthenticationService) => {
fakeAsync(() => {
spyOn(authenticationService, 'logOut');
let navigateSpy = spyOn((<any>component).router, 'navigate');
component.logOut();
expect(mockLogin.logOut).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith(['/log1n']);
});
}));
});
});
I want to check if the user is redirected to the route /login but this test always success even if I put something else than /login

Angular2, jasmine: Component changes not seen in test spec function

I'm still writing tests for my Angular app. I've a test that modifies an Org object, saves the changes, and then proves that the changes have been kept. However, the test isn't seeing the changes.
My mock Org service that saves the changes:
#Injectable()
export class MockOrgService {
constructor() { }
public save(org: Org): Observable<Org> {
let savedOrg: Org = new Org(org);
savedOrg.address2 = 'Saved with id: ' + org.id;
return Observable.of(savedOrg);
}
}
My mock router:
beforeEach(async(() => {
routeStub = { data: Observable.of( { org: org1 } ), snapshot: {} } ;
TestBed.configureTestingModule({
imports: [ FormsModule, RouterTestingModule ],
providers : [
{ provide: DialogService, useClass: MockDialogService },
{ provide: GlobalsService, useClass: MockGlobalsService },
{ provide: OrgService, useClass: MockOrgService },
{ provide: ActivatedRoute, useValue: routeStub }
],
declarations: [ OrgDetailComponent ],
})
.compileComponents();
}));
My component function being tested:
private gotoParent(): void {
this.router.navigate(['../'], { relativeTo: this.route });
}
public save(): void {
this.error = null;
let that = this;
this.orgService
.save(that.org)
.subscribe(
(org: Org): void => {
that.org = org;
that.savedOrg = new Org(org);
that.gotoParent();
},
error => this.error = error
);
}
My test:
it('responds to the Save click by saving the Org and refilling the component', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
comp.org.id = 2;
comp.org.name = 'Another Org';
let elButton = fixture.debugElement.query(By.css('#save'));
elButton.nativeElement.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(comp.error).toBeNull();
expect(comp.savedOrg.id).toEqual(2);
expect(comp.savedOrg.name).toEqual('Another Org');
expect(routeStub).toHaveBeenCalledWith(['../']);
});
});
When I use breakpoints I see that the OrgService.save() is called when click() is run, and that in the component save() function the that.savedOrg is being set. But when the test gets into the expect() functions comp.savedOrg is at its original value. It is as though there are two component instances.
FWIW, after setting, or not setting, my savedOrg my function then tries to route. I instead get an error:
Error: Expected a spy, but got Object({ data: ScalarObservable({ _isScalar: true, value: Object({ org: Org({ id: 2, [SNIP]
I'm not sure what I'm supposed to do to tell that the "goToParent" routing has been called.
Thanks in advance for help,
Jerome.
I figured out the "not seen in test spec function" issue. I am missing a line, right after the first whenStable(), which should be:
comp = fixture.componentInstance;
That makes everything sync OK. Now I must figure out how to make route testing work. That's another job.
Jerome.