angulartypescriptangular2-routingangular2-testing

Angular 2 unit testing components with routerLink


I am trying to test my component with angular 2 final, but I get an error because the component uses the routerLink directive. I get the following error:

Can't bind to 'routerLink' since it isn't a known property of 'a'.

This is the relevant code of the ListComponent template

<a 
  *ngFor="let item of data.list" 
  class="box"
  routerLink="/settings/{{collectionName}}/edit/{{item._id}}">

And here is my test.

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

import { ListComponent } from './list.component';
import { defaultData, collectionName } from '../../config';
import { initialState } from '../../reducers/reducer';


const data = {
  sort: initialState.sort,
  list: [defaultData, defaultData],
};

describe(`${collectionName} ListComponent`, () => {
  let fixture;
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        ListComponent,
      ],
    }).compileComponents(); // compile template and css;
    fixture = TestBed.createComponent(ListComponent);
    fixture.componentInstance.data = data;
    fixture.detectChanges();
  });

  it('should render 2 items in list', () => {
    const el = fixture.debugElement.nativeElement;
    expect(el.querySelectorAll('.box').length).toBe(3);
  });
});

I looked at several answers to similar questions but could not find a solution that worked for me.


Solution

  • You need to configure all the routing. For testing, rather than using the RouterModule, you can use the RouterTestingModule from @angular/router/testing, where you can set up some mock routes. You will also need to import the CommonModule from @angular/common for your *ngFor. Below is a complete passing test

    import { Component } from '@angular/core';
    import { Router } from '@angular/router';
    import { By } from '@angular/platform-browser';
    import { Location, CommonModule } from '@angular/common';
    import { RouterTestingModule } from '@angular/router/testing';
    import { TestBed, inject, async } from '@angular/core/testing';
    
    @Component({
      template: `
        <a routerLink="/settings/{{collName}}/edit/{{item._id}}">link</a>
        <router-outlet></router-outlet>
      `
    })
    class TestComponent {
      collName = 'testing';
      item = {
        _id: 1
      };
    }
    
    @Component({
      template: ''
    })
    class DummyComponent {
    }
    
    describe('component: TestComponent', function () {
      beforeEach(() => {
        TestBed.configureTestingModule({
          imports: [
            CommonModule,
            RouterTestingModule.withRoutes([
             { path: 'settings/:collection/edit/:item', component: DummyComponent }
            ])
          ],
          declarations: [ TestComponent, DummyComponent ]
        });
      });
    
      it('should go to url',
        async(inject([Router, Location], (router: Router, location: Location) => {
    
        let fixture = TestBed.createComponent(TestComponent);
        fixture.detectChanges();
    
        fixture.debugElement.query(By.css('a')).nativeElement.click();
        fixture.whenStable().then(() => {
          expect(location.path()).toEqual('/settings/testing/edit/1');
          console.log('after expect');
        });
      })));
    });
    

    UPDATE

    Another option, if you just want to test that the routes are rendered correctly, without trying to navigate...

    You an just import the RouterTestingModule without configuring any routes

    imports: [ RouterTestingModule ]
    

    then just check that the link is rendered with the correct URL path, e.g.

    let href = fixture.debugElement.query(By.css('a')).nativeElement
        .getAttribute('href');
    expect(href).toEqual('/settings/testing/edit/1');