web-dev-qa-db-ja.com

router.events.subscribe()のモックAngular2

私のapp.component.tsには、次のngOnInit関数があります。

ngOnInit() {
    this.sub = this.router.events.subscribe(e => {
      if (e instanceof NavigationEnd) {
        if (!e.url.includes('login')) {
          this.loggedIn = true;
        } else {
          this.loggedIn = false;
        }
      }
    });
  }

現在、subがnullでないかどうかをテストしていますが、100%のカバレッジで関数をテストしたいです。

ルーターオブジェクトをモックして、URLをシミュレートし、this.loggedInが正しく設定されているかどうかをテストできるようにします。

この機能をどのようにモックするのですか?私はそれを試しましたが、関連するコールバックとNavigationEndでこれをどのように行うかわかりません。

17
stijn.aerts

誰かがそれを探しているなら、私は答えを見つけました:

import {
  addProviders,
  async,
  inject,
  TestComponentBuilder,
  ComponentFixture,
  fakeAsync,
  tick
} from '@angular/core/testing';
import { AppComponent } from './app.component';
import { Router, ROUTER_DIRECTIVES, NavigationEnd } from '@angular/router';
import { HTTP_PROVIDERS } from '@angular/http';
import { LocalStorage, WEB_STORAGE_PROVIDERS } from 'h5webstorage';
import { NavComponent } from '../nav/nav.component';
import { FooterComponent } from '../footer/footer.component';
import { Observable } from 'rxjs/Observable';

class MockRouter {
  public ne = new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });
}

class MockRouterNoLogin {
  public ne = new NavigationEnd(0, 'http://localhost:4200/dashboard', 'http://localhost:4200/dashboard');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });
}
23
stijn.aerts

Angular docsからこのメソッドを使用してテスト用のNavigationEndイベントを実装するルータースタブのバージョンを作成しました。

import {Injectable} from '@angular/core';
import { NavigationEnd } from '@angular/router';
import {Subject} from "rxjs";

@Injectable()
export class RouterStub {
  public url;
  private subject = new Subject();
  public events = this.subject.asObservable();

  navigate(url: string) {
    this.url = url;
    this.triggerNavEvents(url);
  }

  triggerNavEvents(url) {
    let ne = new NavigationEnd(0, url, null);
    this.subject.next(ne);
  }
}
10
ForrestLyman

受け入れられた答えは正しいですが、これは少し簡単です、あなたは置き換えることができます

public ne = new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });

沿って:

public events = Observable.of( new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login'));

そして、問題の機能をテストするための完全なテストファイルを以下で見つけます。

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

/**
 * Load the implementations that should be tested
 */
import { AppComponent } from './app.component';

import { NavigationEnd, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';


class MockServices {
  // Router
  public events = Observable.of( new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login'));
}

describe(`App`, () => {
  let comp: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let router: Router;

  /**
   * async beforeEach
   */
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ AppComponent ],
      schemas: [NO_ERRORS_SCHEMA],
      providers: [
        { provide: Router, useClass: MockServices },
      ]
    })
    /**
     * Compile template and css
     */
    .compileComponents();
  }));

  /**
   * Synchronous beforeEach
   */
  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    comp    = fixture.componentInstance;

    router = fixture.debugElement.injector.get( Router);

    /**
     * Trigger initial data binding
     */
    fixture.detectChanges();
  });

  it(`should be readly initialized`, () => {
    expect(fixture).toBeDefined();
    expect(comp).toBeDefined();
  });

  it('ngOnInit() - test that this.loggedIn is initialised correctly', () => {
    expect(comp.loggedIn).toEqual(true);
  });

});
6
Tonio

前の例public events = Observable.of( new NavigationEnd(0, 'http://localhost..'));は、が文句を言うKarmaに従って機能していないようです

失敗:undefinedはオブジェクトではありません(「router.routerState.root」を評価)rootRoute @ http:// localhost:9876/_karma_webpack_/vendor.bundle.js

(モックされた)ルーターインスタンスeventsにもかかわらず、元のapp.component.tsのngOninit()でサブスクリプションコールバックが正常に実行されています。 Karmaがテスト中のアプリケーションコンポーネント:

_this.sub = this.router.events.subscribe(e => { // successful execution across Karma
_

実際、Routerがモックされた方法は不完全に見え、Karmaの将来のstructureとして不正確に見えます:実行時に未定義になる_router.routerState_のため。

AngularルータはRoutesRecognizedeventsを含む私の側で正確に「スタブ」されています) 私の場合はObservablesとして人工的に焼きます

_class MockRouter {
    public events = Observable.of(new RoutesRecognized(2 , '/', '/',
                                  createRouterStateSnapshot()));
}

const createRouterStateSnapshot = function () {
    const routerStateSnapshot = jasmine.createSpyObj('RouterStateSnapshot', 
                                                     ['toString', 'root']);
    routerStateSnapshot.root = jasmine.createSpyObj('root', ['firstChild']);
    routerStateSnapshot.root.firstChild.data = {
        xxx: false
    };
    return <RouterStateSnapshot>routerStateSnapshot;
};
_

ngOnInit() bodyが期待するものに合うように、深い構造を持つRoutesRecognizedeventが必要です:

_ngOnInit() {
   this.router.events.filter((event) => {
        return event instanceof RoutesRecognized;
    }).subscribe((event: RoutesRecognized) => {
        // if (!event.state.root.firstChild.data.xxx) {
        // RoutesRecognized event... to be baked from specs mocking strategy
   });
}
_

<package.json>コンテンツの要約/要約:

アンギュラー/ルーター:5.2.9、カルマ:2.0.2、ジャスミンコア:2.6.4、カルマジャスミン:1.1.2

1
hugo machefer