Подтвердить что ты не робот

Angular 2 Жасмин Не может связываться с 'routerLink', так как это не известное свойство 'a'

Я создаю unit test для моего компонента Navbar и получаю сообщение об ошибке:

Невозможно связать с 'routerLink', поскольку это не известное свойство 'a'

Компонент Navbar TS

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { NavActiveService } from '../../../services/navactive.service';
import { GlobalEventsManager } from '../../../services/GlobalEventsManager';

@Component({
  moduleId: module.id,
  selector: 'my-navbar',
  templateUrl: 'navbar.component.html',
  styleUrls:['navbar.component.css'],
  providers: [NavActiveService]
})
export class NavComponent {
  showNavBar: boolean = true;

  constructor(private router: Router,
              private navactiveservice:NavActiveService,
              private globalEventsManager: GlobalEventsManager){

    this.globalEventsManager.showNavBar.subscribe((mode:boolean)=>{
      this.showNavBar = mode;
    });

  }

}

Спецификация компонентов Navbar

import { ComponentFixture, TestBed, async } from '@angular/core/testing';    
import { NavComponent } from './navbar.component';
import { DebugElement }    from '@angular/core';
import { By }              from '@angular/platform-browser';
import { Router } from '@angular/router';

export function main() {
    describe('Navbar component', () => {

        let de: DebugElement;
        let comp: NavComponent;
        let fixture: ComponentFixture<NavComponent>;
        let router: Router;

        // preparing module for testing
        beforeEach(async(() => {
            TestBed.configureTestingModule({
                declarations: [NavComponent],
            }).compileComponents().then(() => {

                fixture = TestBed.createComponent(NavComponent);
                comp = fixture.componentInstance;
                de = fixture.debugElement.query(By.css('p'));

            });
        }));


        it('should create component', () => expect(comp).toBeDefined());


/*        it('should have expected <p> text', () => {
            fixture.detectChanges();
            const h1 = de.nativeElement;
            expect(h1.innerText).toMatch(" ");
        });*/


    });
}

Я понимаю, что мне нужно добавить маршрутизатор в качестве шпиона, но если я добавлю его как SpyObj и объявлю его как поставщика, то получаю ту же ошибку.

Есть ли лучший способ добавить исправление этой ошибки?

РЕДАКТИРОВАТЬ: Работа Unit Test

Построил этот unit test на основе ответа:

import { ComponentFixture, TestBed, async  } from '@angular/core/testing';
import { NavComponent } from './navbar.component';
import { DebugElement }    from '@angular/core';
import { By }              from '@angular/platform-browser';
import { RouterLinkStubDirective, RouterOutletStubComponent } from '../../../../test/router-stubs';
import { Router } from '@angular/router';
import { GlobalEventsManager } from '../../../services/GlobalEventsManager';
import { RouterModule } from '@angular/router';
import { SharedModule } from '../shared.module';


export function main() {
    let comp: NavComponent;
    let fixture: ComponentFixture<NavComponent>;
    let mockRouter:any;
    class MockRouter {
        //noinspection TypeScriptUnresolvedFunction
        navigate = jasmine.createSpy('navigate');
    }

    describe('Navbar Componenet', () => {

        beforeEach( async(() => {
            mockRouter = new MockRouter();
            TestBed.configureTestingModule({
                imports: [ SharedModule ]
            })

            // Get rid of app Router configuration otherwise many failures.
            // Doing so removes Router declarations; add the Router stubs
                .overrideModule(SharedModule, {
                    remove: {
                        imports: [ RouterModule ],

                    },
                    add: {
                        declarations: [ RouterLinkStubDirective, RouterOutletStubComponent ],
                        providers: [ { provide: Router, useValue: mockRouter }, GlobalEventsManager ],
                    }
                })

                .compileComponents()

                .then(() => {
                    fixture = TestBed.createComponent(NavComponent);
                    comp    = fixture.componentInstance;
                });
        }));

        tests();
    });


        function tests() {
            let links: RouterLinkStubDirective[];
            let linkDes: DebugElement[];

            beforeEach(() => {
                // trigger initial data binding
                fixture.detectChanges();

                // find DebugElements with an attached RouterLinkStubDirective
                linkDes = fixture.debugElement
                    .queryAll(By.directive(RouterLinkStubDirective));

                // get the attached link directive instances using the DebugElement injectors
                links = linkDes
                    .map(de => de.injector.get(RouterLinkStubDirective) as RouterLinkStubDirective);
            });

            it('can instantiate it', () => {
                expect(comp).not.toBeNull();
            });

            it('can get RouterLinks from template', () => {
                expect(links.length).toBe(5, 'should have 5 links');
                expect(links[0].linkParams).toBe( '/', '1st link should go to Home');
                expect(links[1].linkParams).toBe('/', '2nd link should go to Home');
expect(links[2].linkParams).toBe('/upload', '3rd link should go to Upload');
                expect(links[3].linkParams).toBe('/about', '4th link should to to About');
                expect(links[4].linkParams).toBe('/login', '5th link should go to Logout');
            });

            it('can click Home link in template', () => {
                const uploadLinkDe = linkDes[1];
                const uploadLink = links[1];

                expect(uploadLink.navigatedTo).toBeNull('link should not have navigated yet');

                uploadLinkDe.triggerEventHandler('click', null);
                fixture.detectChanges();

                expect(uploadLink.navigatedTo).toBe('/');
            });


            it('can click upload link in template', () => {
                const uploadLinkDe = linkDes[2];
                const uploadLink = links[2];

                expect(uploadLink.navigatedTo).toBeNull('link should not have navigated yet');

                uploadLinkDe.triggerEventHandler('click', null);
                fixture.detectChanges();

                expect(uploadLink.navigatedTo).toBe('/upload');
            });

            it('can click about link in template', () => {
                const uploadLinkDe = linkDes[3];
                const uploadLink = links[3];

                expect(uploadLink.navigatedTo).toBeNull('link should not have navigated yet');

                uploadLinkDe.triggerEventHandler('click', null);
                fixture.detectChanges();

                expect(uploadLink.navigatedTo).toBe('/about');
            });

            it('can click logout link in template', () => {
                const uploadLinkDe = linkDes[4];
                const uploadLink = links[4];

                expect(uploadLink.navigatedTo).toBeNull('link should not have navigated yet');

                uploadLinkDe.triggerEventHandler('click', null);
                fixture.detectChanges();

                expect(uploadLink.navigatedTo).toBe('/login');
            });
        }
}
4b9b3361

Ответ 1

Документы Angular Testing решают эту проблему с помощью RouterLinkDirectiveStub и RouterOutletStubComponent так что routerLink является известным свойством <a>.

По сути, это говорит о том, что использование RouterOutletStubComponent является безопасным способом тестирования routerLinks на routerLinks без всех сложностей и ошибок при использовании реального RouterOutlet. Ваш проект должен знать, что он существует, поэтому он не выдает ошибок, но ему не нужно ничего делать в этом случае.

RouterLinkDirectiveStub позволяет вам щелкать ссылки <a> с директивой routerLink и получать достаточно информации для проверки того, что на нее нажимают (navigatedTo) и переходят на правильный маршрут (linkParams). Больше функциональности, чем это, и вы действительно больше не тестируете свой компонент отдельно.

Посмотрите их демонстрацию тестов в app/app.component.spec.ts. Возьмите testing/router-link-directive-stub.ts и добавьте в свой проект. Затем вы вставите 2 заглушки в ваши объявления TestBed.

Ответ 2

Просто импортируйте RouterTestingModule в TestBed.configureTestingModule вашего файла спецификаций компонентов file

Например:

import { RouterTestingModule } from '@angular/router/testing';


TestBed.configureTestingModule({
  imports: [RouterTestingModule],
  declarations: [ ComponentHeaderComponent ]
})

Ответ 3

Если вы хотите только изолированный тест и НЕ ЗАБОТИТЕСЬ о шаблоне, вы можете добавить NO_ERRORS_SCHEMA. Это говорит Angular не показывать ошибку, если он встречает какой-либо неизвестный атрибут или элемент в HTML

Например:

TestBed.configureTestingModule({
  declarations: [ ComponentHeaderComponent ],
  schemas: [ NO_ERRORS_SCHEMA ]
})