Как протестировать навигацию по маршрутизации с помощью модуля RouterTestingModule

#angular #typescript #unit-testing #jasmine #angular11

Вопрос:

Я хочу проверить метод, который отправляет меня на определенную страницу. Я проверил несколько примеров, и все они сделаны так же, как и я, но я получаю сообщение об ошибке:

Ожидалось, что spy navigate будет вызван с помощью: [ ‘/themen’], но он так и не был вызван. Ошибка: Ожидалось, что spy navigate был вызван с помощью: [ ‘/themen’], но он так и не был вызван.

Мой html-код выглядит так:

 <button class="arrow-back-icon kf-tooltip-btn align-middle" id="backButton"
         tabindex="0" name="ic_arrow-back_24" (click)="goBack()">
</button>
 

Метод в моем компоненте таков:

 import {AfterViewChecked, ChangeDetectorRef, Component, OnInit} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {SharingDataService} from '../services/sharing-data.service';
import {ActivatedRoute, Router} from '@angular/router';
import {BreakpointObserver} from '@angular/cdk/layout';
@Component({
  selector: 'app-meeting',
  templateUrl: './meeting.component.html',
  styleUrls: ['./meeting.component.scss']
})
export class MeetingComponent implements OnInit, AfterViewChecked {
  constructor(private readonly translate: TranslateService,
              private readonly sharingDataService: SharingDataService,
              private readonly router: Router,
              private readonly activatedRoute: ActivatedRoute,
              private readonly breakpointObserver: BreakpointObserver,
              private readonly cd: ChangeDetectorRef) {
  }

goBack(): void {
  this.router.navigate(['/themen']).then();
}
}
 

Тест заключается в:

 import {ComponentFixture, fakeAsync, inject, TestBed, waitForAsync} from '@angular/core/testing';
import {MeetingComponent} from './meeting.component';
import {Router, Routes} from '@angular/router';
import {CommonModule} from '@angular/common';
import {RouterTestingModule} from '@angular/router/testing';
import {TranslateLocalModule} from '../../../projects/shared/src/lib/translate';

describe('MeetingComponent', () => {
  let sut: meetingComponent;
  let fixture: ComponentFixture<MeetingComponent>;
  const routes: Routes = [
    {path: '', redirectTo: 'themen', pathMatch: 'full'},
    {path: 'meeting', component: meetingComponent},
    {path: '**', redirectTo: 'themen', pathMatch: 'full'}
  ];
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ MeetingComponent ],
      imports: [
        CommonModule,
        RouterTestingModule.withRoutes(routes),
        TranslateLocalModule,
      providers: [
      ]
    }).compileComponents();
  });
  beforeEach(() => {fixture = TestBed.createComponent(MeetingComponent);
    sut = fixture.componentInstance;
    fixture.detectChanges();
  });
  it('should create', () => {
    expect(sut).toBeTruthy();
  });
  it('should go on previous step', waitForAsync(() => {
    const routerSpy = {
      navigate: jasmine.createSpy('navigate')
    };
    sut.onPrevStep();
    fixture.detectChanges();
    expect (routerSpy.navigate).toHaveBeenCalledWith('/themen');
  }));
});
 

Может кто-нибудь сказать мне, что я делаю не так?

Я также попробовал это:

 it('should navigates to previous step', fakeAsync(async () => {
  spyOn(sut, 'onPrevStep').and.returnValue(await Promise.resolve());
  const navigateSpy = spyOn(router, 'navigate');
  sut.onPrevStep();
  tick();
  expect(sut.onPrevStep).toHaveBeenCalled();
  expect(navigateSpy).toHaveBeenCalledWith(['/themen']);
}));
 

Но ошибка та же самая.

Спасибо!

Ответ №1:

Да, я думаю, что ваш routerSpy не связан с маршрутизатором.

Следите за комментариями с !!

Попробуйте это:

 import {ComponentFixture, fakeAsync, inject, TestBed, waitForAsync} from '@angular/core/testing';
import {MeetingComponent} from './meeting.component';
import {Router, Routes} from '@angular/router';
import {CommonModule} from '@angular/common';
import {RouterTestingModule} from '@angular/router/testing';
import {TranslateLocalModule} from '../../../projects/shared/src/lib/translate';

describe('MeetingComponent', () => {
  let sut: meetingComponent;
  let fixture: ComponentFixture<MeetingComponent>;
  // !! add router variable for later use !!
  let router: Router;  

  const routes: Routes = [
    {path: '', redirectTo: 'themen', pathMatch: 'full'},
    {path: 'meeting', component: meetingComponent},
    {path: '**', redirectTo: 'themen', pathMatch: 'full'}
  ];
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ MeetingComponent ],
      imports: [
        CommonModule,
        RouterTestingModule.withRoutes(routes),
        TranslateLocalModule,
      providers: [
      ]
    }).compileComponents();
  });
  beforeEach(() => {
    // !! get a handle on the router !!
    router = TestBed.inject(Router);
    fixture = TestBed.createComponent(MeetingComponent);
    sut = fixture.componentInstance;
    fixture.detectChanges();
  });
  it('should create', () => {
    expect(sut).toBeTruthy();
  });
  it('should go on previous step', waitForAsync(() => {
    // !! spy on the router !!
    spyOn(router, 'navigate');
    // !! I think this should be sut.goBack(); !!
    sut.onPrevStep();
    fixture.detectChanges();
    // !! change expectation !!
    expect(router.navigate).toHaveBeenCalledWith(['/themen']);
  }));
});
 

Комментарии:

1. Привет! @AliF50, я сделал то, что вы сказали, но я получил новую ошибку: Ошибка: Не поймана (в обещании): Ошибка типа: Не удается прочитать свойство «тогда» неопределенного Ошибка типа: Не удается прочитать свойство «тогда» неопределенного

Ответ №2:

В моем методе у меня был этот.router.navigate([‘/themen’]).затем(); вот почему, когда я применил решение @AliF50, оно не сработало. Если я удалю .то() тестовое решение будет работать правильно. В этом случае я могу это сделать, потому что мне не нужно выполнять какой-либо другой код при вызове навигатора.

Но если вы это сделаете, то вы должны предоставить обещанную ценность. Например:

 import {ComponentFixture, fakeAsync, inject, TestBed, waitForAsync} from '@angular/core/testing';
import {MeetingComponent} from './meeting.component';
import {Router, Routes} from '@angular/router';
import {CommonModule} from '@angular/common';
import {RouterTestingModule} from '@angular/router/testing';
import {TranslateLocalModule} from '../../../projects/shared/src/lib/translate';

describe('MeetingComponent', () => {
  let sut: meetingComponent;
  let fixture: ComponentFixture<MeetingComponent>;
  let router: Router;  

  const routes: Routes = [
    {path: '', redirectTo: 'themen', pathMatch: 'full'},
    {path: 'meeting', component: meetingComponent},
    {path: '**', redirectTo: 'themen', pathMatch: 'full'}
  ];
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ MeetingComponent ],
      imports: [
        CommonModule,
        RouterTestingModule.withRoutes(routes),
        TranslateLocalModule,
      providers: [
      ]
    }).compileComponents();
    !!The router spy must be declared here for me it did not work in the before each
    // !! get a handle on the router !!
    router = TestBed.inject(Router);
    // !! spy on the router and return a Promise Value!!
    spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));     
  });
  beforeEach(() => {
    fixture = TestBed.createComponent(MeetingComponent);
    sut = fixture.componentInstance;
    fixture.detectChanges();
    router.initialNavigation();
  });
  it('should create', () => {
    expect(sut).toBeTruthy();
  });
  it('should go on previous step', waitForAsync(() => {
    sut.onPrevStep();
    expect(router.navigate).toHaveBeenCalledWith(['/themen']);
  }));
});