web-dev-qa-db-ja.com

未処理のPromise拒否:どのルートにも一致しません

この単体テストを実行すると:

it('can click profile link in template', () => {
    const landingPageLinkDe = linkDes[0];
    const profileLinkDe = linkDes[1];
    const aboutLinkDe = linkDes[2];
    const findLinkDe = linkDes[3];
    const addLinkDe = linkDes[4];
    const registerLinkDe = linkDes[5];
    const landingPageLinkFull = links[0];
    const profileLinkFull = links[1];
    const aboutLinkFull = links[2];
    const findLinkFull = links[3];
    const addLinkFull = links[4];
    const registerLinkFull = links[5];

    navFixture.detectChanges();
    expect(profileLinkFull.navigatedTo)
        .toBeNull('link should not have navigated yet');
    profileLinkDe.triggerEventHandler('click', { button: 0 });
    landingPageLinkDe.triggerEventHandler('click', { button: 0 });
    aboutLinkDe.triggerEventHandler('click', { button: 0 });
    registerLinkDe.triggerEventHandler('click', { button: 0 });
    findLinkDe.triggerEventHandler('click', { button: 0 });
    addLinkDe.triggerEventHandler('click', { button: 0 });

    navFixture.detectChanges();
    expect(landingPageLinkFull.navigatedTo).toBe('/');
    expect(profileLinkFull.navigatedTo).toBe('/profile');
    expect(aboutLinkFull.navigatedTo).toBe('/about');
    expect(findLinkFull.navigatedTo).toBe('/find');
    expect(addLinkFull.navigatedTo).toBe('/add');
    expect(registerLinkFull.navigatedTo).toBe('/register');
});

私はこのエラーを受け取ります:

zone.js:388未処理のPromise拒否:どのルートにも一致しません。 URLセグメント: '追加';ゾーン:ProxyZone;タスク:Promise.then;値:エラー:どのルートにも一致しません。 URLセグメント:「追加」(…)エラー:どのルートにも一致しません。 URLセグメント:「追加」

テストはまだ成功していますが、なぜエラーが発生するのかを知ることは興味深いでしょう。ユーザーのようにアプリケーションを使用してもエラーは発生しません。私はエラーを調査しましたが、それは通常、ルートにデフォルトのパスを提供していないことが原因ですが、私はそれをしました。

このエラーを引き起こすために何か間違ったことをしていますか?

navbar.component.spec.ts

import 'zone.js/dist/long-stack-trace-zone.js';
import 'zone.js/dist/async-test.js';
import 'zone.js/dist/fake-async-test.js';
import 'zone.js/dist/sync-test.js';
import 'zone.js/dist/proxy.js';
import 'zone.js/dist/jasmine-patch.js';

import {
    ComponentFixture,
    TestBed,
    async,
    fakeAsync
} from '@angular/core/testing';
import {
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
import { By } from '@angular/platform-browser';
import {
    DebugElement,
    Component,
    ViewChild,
    Pipe,
    PipeTransform,
    CUSTOM_ELEMENTS_SCHEMA,
    NO_ERRORS_SCHEMA
} from '@angular/core';
import { DatePipe } from '@angular/common';
import { Router, RouterOutlet, RouterModule } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { NavbarComponent } from './navbar.component';
import { RouterLinkStubDirective } from '../../router-stubs';
import { click } from '../../test/utilities.spec';

describe('NavbarComponent', () => {
    let navComponent: NavbarComponent;
    let navFixture: ComponentFixture<NavbarComponent>;
    let linkDes: any;
    let links: any;
    let landingPageLink: any;
    let profileLink: any;
    let aboutLink: any;
    let findLink: any;
    let addLink: any;
    let registerLink: any;

    beforeAll(() => {
        TestBed.resetTestEnvironment();
        TestBed.initTestEnvironment(BrowserDynamicTestingModule,
            platformBrowserDynamicTesting());
    });

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [
                NavbarComponent,
                RouterLinkStubDirective
            ],
            imports: [RouterTestingModule],
            schemas: [NO_ERRORS_SCHEMA]
        }).compileComponents();
    }));

    beforeEach(() => {
        navFixture = TestBed.createComponent(NavbarComponent);
        navComponent = navFixture.componentInstance;
        navFixture.detectChanges();
        linkDes = navFixture.debugElement
            .queryAll(By.directive(RouterLinkStubDirective));
        links = linkDes
            .map((de: any) => de.injector
                .get(RouterLinkStubDirective) as RouterLinkStubDirective);
        landingPageLink = links[0].linkParams;
        profileLink = links[1].linkParams;
        aboutLink = links[2].linkParams;
        findLink = links[3].linkParams;
        addLink = links[4].linkParams;
        registerLink = links[5].linkParams;
    });

    it('can get RouterLinks from template', () => {
        expect(links.length).toBe(6, 'should have 6 links');
        expect(landingPageLink[0])
            .toEqual('/', '1st link should go to landing page');
        expect(profileLink[0])
            .toEqual('/profile', '2nd link should go to profile');
        expect(aboutLink[0])
            .toEqual('/about', '3rd link should go to about');
        expect(findLink[0])
            .toEqual('/find', '4th link should go to find');
        expect(addLink[0])
            .toEqual('/add', '5th link should go to add');
        expect(registerLink[0])
            .toEqual('/register', '6th link should go to register');
    });

    it('can click profile link in template', () => {
        const landingPageLinkDe = linkDes[0];
        const profileLinkDe = linkDes[1];
        const aboutLinkDe = linkDes[2];
        const findLinkDe = linkDes[3];
        const addLinkDe = linkDes[4];
        const registerLinkDe = linkDes[5];
        const landingPageLinkFull = links[0];
        const profileLinkFull = links[1];
        const aboutLinkFull = links[2];
        const findLinkFull = links[3];
        const addLinkFull = links[4];
        const registerLinkFull = links[5];

        navFixture.detectChanges();
        expect(profileLinkFull.navigatedTo)
            .toBeNull('link should not have navigated yet');
        profileLinkDe.triggerEventHandler('click', { button: 0 });
        landingPageLinkDe.triggerEventHandler('click', { button: 0 });
        aboutLinkDe.triggerEventHandler('click', { button: 0 });
        registerLinkDe.triggerEventHandler('click', { button: 0 });
        findLinkDe.triggerEventHandler('click', { button: 0 });
        addLinkDe.triggerEventHandler('click', { button: 0 });

        navFixture.detectChanges();
        expect(landingPageLinkFull.navigatedTo).toBe('/');
        expect(profileLinkFull.navigatedTo).toBe('/profile');
        expect(aboutLinkFull.navigatedTo).toBe('/about');
        expect(findLinkFull.navigatedTo).toBe('/find');
        expect(addLinkFull.navigatedTo).toBe('/add');
        expect(registerLinkFull.navigatedTo).toBe('/register');
    });
});

テスト用のスタブ:

import 'zone.js/dist/long-stack-trace-zone.js';
import 'zone.js/dist/async-test.js';
import 'zone.js/dist/fake-async-test.js';
import 'zone.js/dist/sync-test.js';
import 'zone.js/dist/proxy.js';
import 'zone.js/dist/jasmine-patch.js';

import {
   EventEmitter,
   Output,
   trigger,
   state,
   style,
   transition,
   animate,
   Directive,
   Input
} from '@angular/core';

import {
   ComponentFixture,
   TestBed,
   async,
   fakeAsync
} from '@angular/core/testing';
import {
   BrowserDynamicTestingModule,
   platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
import { By } from '@angular/platform-browser';
import {
   DebugElement,
   Component,
   ViewChild,
   Pipe,
   PipeTransform
} from '@angular/core';
import { DatePipe } from '@angular/common';
import { Router } from '@angular/router';
import { NavbarComponent } from './shared/subcomponents/navbar.component';
import { AppComponent } from './app.component';
import { click } from './test/utilities.spec';

import { FormsModule, ReactiveFormsModule } from '@angular/forms'

@Directive({
  selector: '[routerLink]',
  Host: {
    '(click)': 'onClick()'
  }
})
export class RouterLinkStubDirective {
  @Input('routerLink') linkParams: any;
  navigatedTo: any = null;

  onClick() {
    this.navigatedTo = this.linkParams[0];
  }
}

app.routes.ts:

import { Routes }  from '@angular/router';
import { LandingPageComponent } from './landing-page/landing-page.component';
import { FindPageComponent } from './find-page/find-page.component';
import { AddPageComponent } from './add-page/add-page.component';
import { RegisterPageComponent } from './register-page/register-page.component';
import { AboutPageComponent } from './about-page/about-page.component';
import { ProfilePageComponent } from './profile-page/profile-page.component';

export const routerConfig: Routes = [
  {
    path: '',
    component: LandingPageComponent
  },
  {
    path: '',
    redirectTo: '',
    pathMatch: 'full'
  },
  {
    path: 'find',
    component: FindPageComponent
  },
  {
    path: 'add',
    component: AddPageComponent
  },
    {
    path: 'register',
    component: RegisterPageComponent
  },
    {
    path: 'about',
    component: AboutPageComponent
  },
    {
    path: 'profile',
    component: ProfilePageComponent
  }
];

navbar.component.html:

<nav class="navbar navbar-dark navbar-fixed-top text-uppercase">
    <div class="container-fluid">
        <button     class="navbar-toggler hidden-md-up pull-xs-right" 
                    type="button" 
                    data-toggle="collapse" 
                    data-target="#nav-content">
                    &#9776;
        </button>
        <a class="navbar-brand" [routerLink]="['/']" 
        routerLinkActive="active">vepo</a>
        <div class="collapse navbar-toggleable-sm" id="nav-content">
            <ul class="nav navbar-nav pull-xs-right">
                 <li class="nav-item">
                    <a class="nav-link" [routerLink]="['/profile']" 
                    routerLinkActive="active">profile</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" [routerLink]="['/about']" 
                    routerLinkActive="active">about</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" [routerLink]="['/find']" 
                    routerLinkActive="active">find</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" [routerLink]="['/add']" 
                    routerLinkActive="active">add</a>
                </li>
                <li class="nav-item">
                    <button type="button" class="as-text nav-link 
                    text-uppercase" (click)="openModal()">
                        login
                    </button>
                </li>
                <li class="nav-item">
                    <a class="nav-link signup" [routerLink]="['/register']" 
                    routerLinkActive="active">sign up free</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<login #modal></login>
<router-outlet></router-outlet>
18

最近同じ問題がありました。これは、コンポーネントのngOnInitメソッド内の_router.navigate_の呼び出しが原因でした。テストはコンポーネントを作成しようとしましたが、ngOnInit内でコンポーネントから移動しようとしました(特定の条件が満たされていないため)。

私の場合は、RouterTestingModuleを_TestBed.configureTestingModule_の一部としてインポートしています。したがって、これを修正するには、RouterTestingModuleを使用してルートを登録しました。たとえば、ナビゲーション呼び出しがrouter.navigate(['example'])のようになり、ExampleComponentに解決されるとします。次のようにテストを設定できます。

_RouterTestingModule.withRoutes([
    { path: 'example', component: ExampleComponent}
])
_

上記を実行すると、_Cannot match any routes_エラーを発行せずにテストを実行できました。

それだけの価値があるのであれば、ルーターをスタブ化し、navigateへの適切な呼び出しが行われていることを確認するのがより良い方法だと思います。

57
spoida

Spoidaの答えを少し拡張するために、私はスタブ付きルーターを使用することになりました。

class RouterStub {
    url = '';
    navigate(commands: any[], extras?: any) { }
}

そしてあなたのテストのためにあなたのプロバイダーで:

providers: [
    { provide: Router, useClass: RouterStub }
    // ... other providers
]

これにより、ユニットテストは引き続き成功し、コンソールエラーが表示されなくなりました。

9
Waggles