I am trying to write some tests for a component that calls router.navigate()
and I'm stuck on errors with declaring the routes. I've read a lot of things and tried all of them but they all lead to some error or another. I am using Angular 4.0.0.
Component:
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
constructor(
private activatedRoute: ActivatedRoute,
private authService: AuthService,
private formBuilder: FormBuilder,
private jwtService: JwtService,
private router: Router,
private storageService: StorageService
) { ... }
ngOnInit() {
}
private login(formData: any): void {
const credentials: any = {
email: formData.controls.email.value,
password: formData.controls.password.value
};
this.authService.login(credentials).subscribe(res => {
this.activatedRoute.params.subscribe(params => {
if (params.returnUrl) {
this.router.navigate([params.returnUrl]);
} else {
this.router.navigate(['/dashboard']);
}
});
}, error => { ... });
}
}
Test:
describe('LoginComponent', () => {
let component: any;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
imports: [
SharedModule,
RouterTestingModule
],
providers: [{
provide: AuthService,
useClass: MockAuthService
}, {
provide: JwtService,
useClass: MockJwtService
}, {
provide: StorageService,
useClass: MockStorageService
}],
schemas: [ NO_ERRORS_SCHEMA ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
describe('login', () => {
it('should call router.navigate with the dashboard route if the login is successful', () => {
spyOn(component.router, 'navigate');
component.authService.login.and.returnValue(Observable.of({ access_token: 'fake_token' }));
component.login(component.loginForm);
expect(component.router.navigate).toHaveBeenCalledWith(['/dashboard']);
});
});
});
This all gives me the following error:
zone.js:569 Unhandled Promise rejection: Cannot match any routes. URL Segment: 'dashboard'
So from there I looked into adding the route with withRoutes
. I don't like that I need to include the DashboardComponent
since it seems like there should be some mocked/blank component available for this especially since I don't want to actually navigate and load another route but I couldn't find anything like that:
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
imports: [
SharedModule,
RouterTestingModule.withRoutes([{
path: 'dashboard',
component: DashboardComponent
}])
],
...
})
.compileComponents();
However that just give me a new error:
Component DashboardComponent is not part of any NgModule or the module has not been imported into your module.
So I thought that maybe I needed to declare the DashboardComponent
, so I added it to the declarations array:
TestBed.configureTestingModule({
declarations: [ LoginComponent, DashboardComponent ],
..
})
.compileComponents();
However that just lead to yet another error:
Unhandled Promise rejection: Cannot find primary outlet to load 'DashboardComponent'
At this point it seems like there must be a simpler way to do this as it's a very common scenario but I've tried everything others say they used and everything just leads further down this rabbit hole.
The solution turned out to be really simple...
Simply adding the RouterTestingModule was almost there, only I needed to spy on router.navigate in all tests to prevent them from trying to actually navigate to another route.
describe('LoginComponent', () => {
let component: any;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
imports: [
SharedModule,
RouterTestingModule // This provides the mock router, location and routerLink
],
providers: [{
provide: AuthService,
useClass: MockAuthService
}, {
provide: JwtService,
useClass: MockJwtService
}, {
provide: StorageService,
useClass: MockStorageService
}],
schemas: [ NO_ERRORS_SCHEMA ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
spyOn(component.router, 'navigate'); // This prevents every test from calling the real router.navigate which means I don't need to add routes to RouterTestingModule
});
describe('login', () => {
it('should call router.navigate with the dashboard route if the login is successful', () => {
spyOn(component.router, 'navigate');
component.authService.login.and.returnValue(Observable.of({ access_token: 'fake_token' }));
component.login(component.loginForm);
expect(component.router.navigate).toHaveBeenCalledWith(['/dashboard']);
});
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With