具有输入绑定和路由器链接的单元测试角度组件给出错误“无法读取未定义的属性'__source'”

时间:2019-09-10 00:04:18

标签: angular unit-testing jasmine

我正在尝试为具有@Input属性的组件编写单元测试,该组件恰好包含带有[routerLink]的锚标记。

我遇到了错误:

Failed: Template parse errors: Can't bind to 'routerLink' since it isn't a known property of 'a'.

要解决此问题,我添加了RouterTestingModule,现在出现此错误:

TypeError: Cannot read property '__source' of undefined

每次测试

Component.ts

import {Component, Input, OnInit} from '@angular/core';
import {Tile, TileDefinition} from "../../model/tile";
import {TilesService} from "../../services/tiles.service";
import {Observable} from "rxjs";

@Component({
  selector: 'app-tile',
  templateUrl: './tile.component.html',
  styleUrls: ['./tile.component.scss']
})
export class TileComponent implements OnInit {

  @Input() tile: Tile;

  percentComplete: string;

  detailsExposed: boolean = false;
  public tileDescription: Observable<TileDefinition>;


  constructor(private tileService: TilesService) { }

  ngOnInit() {
    let num = parseInt(this.tile.custom3);
    if(isNaN(num)){
      this.percentComplete = '0%';
    } else {
      this.percentComplete = parseInt(this.tile.custom3).toString() + '%';
    }
  }

  showDetails(): void {
    this.detailsExposed = true;
    this.tileDescription = this.tileService.getTileDefinition(this.tile.id);
  }

  hideDetails(): void {
    this.detailsExposed = false;
  }
}

Component.html

<div class="row no-gutters mx-1 py-2 border-bottom">
  <div class="col-1">{{ tile.id }}</div>
  <div class="col">{{ tile.name }}</div>
  <div class="col-2">
    <div class="form-row">
      <div class="col">
        <div class="progress">
          <div class="progress-bar"
               [ngClass]="{'bg-success': percentComplete == '100%'}"
               [ngStyle]="{'width': percentComplete}">
            {{percentComplete}}
          </div>
        </div>
      </div>

      <div class="col-auto">
        <button class="btn btn-xs btn-light border text-primary show_more_button" *ngIf="!detailsExposed" (click)="showDetails()">More
        </button>
        <button class="btn btn-xs btn-light border text-primary show_less_button" *ngIf="detailsExposed" (click)="hideDetails()">Less
        </button>
      </div>
    </div>
  </div>
</div>

<div class="p-3 mx-1 border rounded-bottom bg-white" *ngIf="detailsExposed">
  <ng-container *ngIf="tileDescription | async; else loading">

    <a [routerLink]="['/fileeditor','tile',tile.id,'json','definitions']" class="btn btn-sm btn-info">Edit Configuration</a>

  </ng-container>

  <ng-template #loading>
    <div class="col-12 text-center"><p class="display-4 text-muted">Loading</p></div>
  </ng-template>

</div>

测试规范

import {async, ComponentFixture, TestBed} from '@angular/core/testing';

import {TileComponent} from './tile.component';
import {TilesService} from "../../services/tiles.service";
import {Component, DebugElement, ViewChild} from "@angular/core";
import {Tile, TileDefinition} from "../../model/tile";
import {By} from "@angular/platform-browser";
import {RouterTestingModule} from "@angular/router/testing";
import {of} from "rxjs";

// mock a host container so we can input bind to the unit under test
@Component({
  template: '<app-tile [tile]="tile"></app-tile>'
})
class HostComponent{
  tile: Tile = {
    id: "5",
    name: "TileName",
    custom3: "20% imaged"
  };
  @ViewChild(TileComponent) tileComponent: TileComponent;
}

describe('TileComponent', () => {
  let component: HostComponent;
  let fixture: ComponentFixture<HostComponent>;
  let tilesServiceSpy: { getTileDefinition: jasmine.Spy }; // we'll use a spy in place of the real service

  beforeEach(async(() => {
    // create the spy to inject as a substitute for the real TileService
    const tilesService = jasmine.createSpyObj('TilesService', ['getTileDefinition']);

    TestBed.configureTestingModule({
      declarations: [ HostComponent, TileComponent ],
      providers: [{ provide: TilesService, useValue: tilesServiceSpy}],
      imports: [RouterTestingModule.withRoutes([])]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HostComponent);
    component = fixture.componentInstance;
    tilesServiceSpy = TestBed.get(tilesServiceSpy); // we want to get the 'real' instance from our env
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should set percentcomplete on init', () => {
    expect(component.tileComponent.percentComplete).toBeUndefined('starts undefined');
    fixture.detectChanges(); // ngOnInit
    expect(component.tileComponent.percentComplete).toEqual('60%', 'set percentCompete on init')
  });
});

1 个答案:

答案 0 :(得分:1)

在这种情况下,您用于tilesServiceSpy = TestBed.get(tilesServiceSpy);的行应改为tilesServiceSpy = TestBed.get(TilesService);

更改之后,您将可以继续。