Angular 2 Unit Testing - Unable to read the root property from undefined

Error description

Angular version: 2.3.1

My unit test could not create the component - I know that this problem is related to the [routerLink] and [routerLinkActive] , because removing them from the template allows the test to create the component.

TEMPLATE

 <nav class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="container-fluid"> <div class="navbar-header"> <button class="navbar-toggle" data-toggle="collapse" data-target="#iotahoe-top-navigation"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" [routerLink]="['/']">IoTahoe</a> </div> <div class="collapse navbar-collapse" id="iotahoe-top-navigation"> <ul *ngIf="isAuthenticated()" class="nav navbar-nav navbar-right"> <li [routerLinkActive]="['active']"><a [routerLink]="['/dashboard']">Dashboard</a></li> <li [routerLinkActive]="['active']"><a [routerLink]="['/browse']">Browse</a></li> <li [routerLinkActive]="['active']"><a [routerLink]="['/admin']">Admin</a></li> <li [routerLinkActive]="['active']"><a (click)="onLogout()" style="cursor: pointer;">Logout</a></li> </ul> </div> 

TYPESCRIPT

 import { Component, OnInit } from '@angular/core'; import { AuthenticationService } from '../../authentication/authentication.service'; import { Router } from '@angular/router'; @Component({ moduleId: module.id.toString(), selector: 'app-top-navbar', templateUrl: './top-navbar.component.html', styleUrls: ['./top-navbar.component.css'] }) export class TopNavbarComponent implements OnInit { constructor(private authenticationService: AuthenticationService, private router: Router) { } ngOnInit() { } isAuthenticated() { return this.authenticationService.isLoggedIn; } onLogout() { this.authenticationService.logout().subscribe(() => { return this.router.navigate(['/login']); }); } } 

TEST SPEC

 /* tslint:disable:no-unused-variable */ import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import {DebugElement, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, Component} from '@angular/core'; import { RouterTestingModule } from '@angular/router/testing'; import { Location, CommonModule } from '@angular/common'; import { TopNavbarComponent } from './top-navbar.component'; import { AuthenticationService } from '../../authentication/authentication.service'; import { Router } from '@angular/router'; import {ReactiveFormsModule} from "@angular/forms"; @Component({ template: '' }) class DummyComponent { } describe('TopNavbarComponent', () => { let component: TopNavbarComponent; let fixture: ComponentFixture<TopNavbarComponent>; let authenticationService: AuthenticationService; beforeEach(async(() => { const authenticationServiceStub = { isLoggedIn: false }; const routerStub = { navigate: jasmine.createSpy('navigate'), navigateByUrl: jasmine.createSpy('navigateByUrl') }; TestBed.configureTestingModule({ declarations: [ TopNavbarComponent, DummyComponent ], imports:[CommonModule, ReactiveFormsModule, RouterTestingModule.withRoutes( [ { path: '/', component:DummyComponent }, { path: '/login', component:DummyComponent }, { path: '/dashboard', component:DummyComponent }, { path: '/browse', component:DummyComponent }, { path: '/admin', component:DummyComponent } ])], providers: [ { provide: AuthenticationService, useValue: authenticationServiceStub }, { provide: Router, useValue: routerStub } ] }).compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(TopNavbarComponent); component = fixture.componentInstance; authenticationService = TestBed.get(AuthenticationService); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); }); 

ERROR

zone.js: 155 Error on failures: error in package: 407: 9: 6 caused by: Can not read the "root" property from undefined in ViewWrappedError.Error (native) in ViewWrappedError.ZoneAwareError (localhost: 9876 / base / src / test .ts: 133296: 33) in ViewWrappedError.BaseError [as a constructor] (localhost: 9876 / base / src / test.ts: 35630: 16) in ViewWrappedError.WrappedError [as a constructor] (localhost: 9876 / base / src /test.ts: 35695: 16) with a new ViewWrappedError (localhost: 9876 / base / src / test.ts: 68018: 16) in DebugAppView._rethrowWithContext (localhost: 9876 / base / src / test.ts: 108242: 23) in DebugAppView.create (localhost: 9876 / base / src / test.ts: 108142: 18) in DebugAppView.View_TopNavbarComponent_Host0.createInternal (/DynamicTestModule/TopNavbarComponent/host.ngfactory.js:16:19.AppugAppView.ppost.ppostppppppppppppppppppppapost.ppostapppppppppppppppapostaphostapngapreppappaostappostap.ppostap.appostap.appostap.appostap.pact localhost: 9876 / base / src / test.ts: 107700: 21 ) in DebugAppView.createHostView (localhost: 9876 / base / src / test.ts: 108156: 52) on ComponentFactory.create (localhost: 9876 / base / src / test.ts: 49830: 25) in initComponent (localhost: 9876 / base / src / test.ts: 6425: 53) in ZoneDelegate.invoke (localhost: 9876 / base / src / test.ts: 132727: 26) in ProxyZoneSpec.onInvoke (localhost: 9876 / base / src / test.ts: 95802: 39) in ZoneDelegate.invoke (localhost: 9876 / base / src / test.ts: 132726: 32) Zone.runTask @ zone.js: 155ZoneTask.invoke @ zone.js: 345data.args. (anonymous function) @ zone.js: 1376

+4
source share
2 answers

The routerLink directives need a real router, but you are mocking it. A few things that I see you are doing:

  • If you do not plan to click links during testing, you can mock these directives to just create noop directives so that the compiler does not complain. eg.

     @Directive({ selector: '[routerLink], [routerLinkActive]' }) class DummyRouterLinkDirective {} 

    Then just add this to the declarations test module. In this case, you do not need to configure the RouterTestingModule at all. You could get rid of this.

  • Also, if you do not plan to click the test, another option (without the need to create dummy directives should simply ignore the errors of the missing directives:

     schemas: [ NO_ERRORS_SCHEMA ] 

    You would add this to the test module configuration (as shown here ). In some cases, this may be undesirable, as it can also ignore errors that you really want to detect, which can lead to debugging tests.

  • If you really want to click links and check routing, you can use a real router. You can see an example of how you can test navigation using Location , as shown in this post .

+7
source

For me, the solution with non-modern routing worked. But I found out that I also need to add

 <router-outlet></router-outlet> 

for a component that uses an "active router role".

0
source

Source: https://habr.com/ru/post/1271971/


All Articles