【问题标题】:Angular 2 Unit Testing - Cannot read property 'root' of undefinedAngular 2 单元测试 - 无法读取未定义的属性“根”
【发布时间】:2017-02-04 11:18:38
【问题描述】:

错误说明

Angular 版本:2.3.1

我的单元测试无法创建组件 - 我知道这个问题与 [routerLink][routerLinkActive] 指令有关,因为从模板中删除它们允许测试创建组件。

模板

<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>

类型脚本

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']);
    });
}

}

测试规范

/* 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();
});
});

错误

zone.js:155 未捕获的错误:包中的错误:407:9:6 原因:不能 读取未定义的属性“根” 在 ViewWrappedError.Error (本机) 在 ViewWrappedError.ZoneAwareError (localhost:9876/base/src/test.ts:133296:33) 在 ViewWrappedError.BaseError [作为构造函数] (localhost:9876/base/src/test.ts:35630:16) 在 ViewWrappedError.WrappedError [作为构造函数] (localhost:9876/base/src/test.ts:35695:16) 在新的 ViewWrappedError (localhost:9876/base/src/test.ts:68018:16) 在 DebugAppView._rethrowWithContext (localhost:9876/base/src/test.ts:108242:23) 在 DebugAppView.create (localhost:9876/base/src/test.ts:108142:18) 在 DebugAppView.View_TopNavbarComponent_Host0.createInternal (/DynamicTestModule/TopNavbarComponent/host.ngfactory.js:16:19) 在 DebugAppView.AppView.createHostView (localhost:9876/base/src/test.ts:107700:21) 在 DebugAppView.createHostView (localhost:9876/base/src/test.ts:108156:52) 在 ComponentFactory.create (localhost:9876/base/src/test.ts:49830:25) 在 initComponent (localhost:9876/base/src/test.ts:6425:53) 在 ZoneDelegate.invoke (localhost:9876/base/src/test.ts:132727:26) 在 ProxyZoneSpec.onInvoke (localhost:9876/base/src/test.ts:95802:39) 在 ZoneDelegate.invoke (localhost:9876/base/src/test.ts:132726:32)Zone.runTask @ zone.js:155ZoneTask.invoke@zone.js:345data.args.(匿名函数) @zone.js:1376

【问题讨论】:

    标签: unit-testing angular


    【解决方案1】:

    routerLink 指令需要一个真正的路由器,但你在嘲笑它。我可以看到你在做几件事:

    • 如果您不打算在测试期间单击链接,那么您可以模拟这些指令以仅生成“noop”指令,这样编译器就不会抱怨。例如

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

      然后将其添加到测试模块的declarations。有了这个,你根本不需要配置RouterTestingModule。你可能会摆脱它。

    • 另外,如果您不打算点击测试,另一种选择(无需创建虚拟指令就是忽略缺少指令的错误:

      schemas: [ NO_ERRORS_SCHEMA ]
      

      您可以将此添加到测试模块配置中(如 here 所示)。在某些情况下,这可能是不可取的,因为它也可能会忽略您实际希望检测到的错误,这可能会导致难以调试测试。

    • 如果你真的想点击链接并测试路由,那么你可以使用真正的路由器。您可以查看如何使用Location 测试导航的示例,如this post 所示。

    【讨论】:

    • 您好,感谢您的回复,我添加了 mock 指令并将其包含在测试模块的声明中,但同样的问题仍然导致测试失败。
    • 你删除了RouterTestingModule吗?
    • 不,我没有,但是当我这样做时,它起作用了!太棒了,感谢您的帮助。
    • 想一想,我认为只导入RouterTestingModule(不调用withRoutes)也应该可以。我可能错了,只需要编译真正的路由器。我想也许调用withRoutes 需要真正的路由器。您可以尝试只导入路由器测试模块,这样您就不需要虚拟指令。我认为这将是首选方式
    • 在我的例子中,我的组件也在导入ActivatedRoute,而不是引发像Missing provider for ActivatedRoute这样的错误,而是引发了这个根错误。
    【解决方案2】:

    对我来说,非模拟路由的解决方案有效。但是我发现我还需要添加一个

    <router-outlet></router-outlet>
    

    使用“routerlink active”到组件。

    【讨论】:

      猜你喜欢
      • 2017-04-04
      • 2017-06-28
      • 1970-01-01
      • 2019-01-08
      • 2017-03-01
      • 2018-12-23
      • 2021-01-22
      • 2018-04-18
      • 1970-01-01
      相关资源
      最近更新 更多