ActivatedRouteのパラメーターに依存するコンポーネントを単体テストするにはどうすればよいですか?


93

オブジェクトの編集に使用されるコンポーネントの単体テストを行っています。オブジェクトには、idサービスでホストされているオブジェクトの配列から特定のオブジェクトを取得するために使用される一意のオブジェクトがあります。特定のものidは、ルーティングを介して、特にActivatedRouteクラスを介して渡されるパラメーターを介して調達されます。

コンストラクターは次のとおりです。

 constructor(private _router:Router, private _curRoute:ActivatedRoute, private _session:Session) {
}

ngOnInit() {
    this._curRoute.params.subscribe(params => {
        this.userId = params['id'];
        this.userObj = this._session.allUsers.filter(user => user.id.toString() === this.userId.toString())[0];

このコンポーネントで基本的な単体テストを実行したいと思います。ただし、idパラメータを挿入する方法がわからないため、コンポーネントにこのパラメータが必要です。

ちなみに、私はすでにSessionサービスのモックを持っているので、心配はいりません。

回答:


135

これを行う最も簡単な方法は、useValue属性を使用して、モックする値のObservableを提供することです。

RxJS <6

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
...
{
  provide: ActivatedRoute,
  useValue: {
    params: Observable.of({id: 123})
  }
}

RxJS> = 6

import { of } from 'rxjs';
...
{
  provide: ActivatedRoute,
  useValue: {
    params: of({id: 123})
  }
}

11
Observable.ofは私には存在しません!:S
アレハンドロサンスディアス2016

4
rxjs / ObservableからObservableをインポート
zmanc 2016

6
このコードは私のプロジェクトでこのエラーをUncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'ng:///DynamicTestModule/HomeContentComponent.ngfactory.js'. at http://localhost:9876/_karma_webpack_/polyfills.bundle.js:2605
引き起こし

5
RxJs6ofは単独で使用する必要があります。またRouterTestingModule、この回答のコードの代わりに使用する可能性があります。
ベンラシコット2018年

5
@BenRacicotこの回答は、RxJs6が存在する前に提供されました。また、代わりに「代わりにこれを行う」と言うと、直接賛成できる答えが得られます。
zmanc

18

私はこれを行う方法を理解しました!

ActivatedRouteはサービスですので、そのためのモックサービスを確立することができます。このモックサービスと呼びましょうMockActivatedRoute。次のように拡張ActivatedRouteMockActivatedRouteます。

class MockActivatedRoute extends ActivatedRoute {
    constructor() {
        super(null, null, null, null, null);
        this.params = Observable.of({id: "5"});
    }

この行super(null, ....)は、4つの必須パラメーターを持つスーパークラスを初期化します。ただし、この場合、これらのパラメーターは何も必要ないため、null値に初期化します。私たちが必要なのはの値です。したがって、を使用して、の値をオーバーライドし、テスト対象が依存しているパラメータの値になるように初期化します。paramsObservable<>this.paramsparamsObservable<>

次に、他のモックサービスと同様に、それを初期化し、コンポーネントのプロバイダーをオーバーライドします。

幸運を!


1
私は今これに直面しています!ただし、superまたはを使用しようとするとエラーが発生しますObservable。これらはどこから来たのですか?
aarmora 2016

super()が組み込まれています。バージョンObservableから、rxjs/Observableまたはrxjsバージョンによって異なります。を使用して取得しimport {Observable} from 'rxjs'ます。
oooyaya 2017年

あなたは1つの答えを受け入れ、別の答えを投稿しました...これがハイランダーだった場合(そして1つしか存在できなかった場合)、「本当に」選んだのはどれですか。その理由は何ですか。つまり、これは本質的に、あなたが受け入れたzmancの答えと同じものに還元されると思います。この[少し]より複雑なモックを設定することで、付加価値を見つけましたか?
ラフィン

11

角度8+にはRouterTestingModuleがあり、コンポーネントのActivatedRouteまたはRouterにアクセスするために使用できます。また、RouterTestingModuleにルートを渡し、要求されたルートメソッドのスパイを作成することもできます。

たとえば、私のコンポーネントには次のものがあります。

ngOnInit() {
    if (this.route.snapshot.paramMap.get('id')) this.editMode()
    this.titleService.setTitle(`${this.pageTitle} | ${TAB_SUFFIX}`)
}

そして私のテストでは:

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

  beforeEach(() => {
    router = TestBed.get(Router)
    route = TestBed.get(ActivatedRoute)
  })

その後、「it」セクションで:

  it('should update', () => {
    const spyRoute = spyOn(route.snapshot.paramMap, 'get')
    spyRoute.and.returnValue('21')
    fixture = TestBed.createComponent(ProductLinePageComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
    expect(component).toBeTruthy()
    expect(component.pageTitle).toBe('Edit Product Line')
    expect(component.formTitle).toBe('Edit Product Line')
    // here you can test the functionality which is triggered by the snapshot
  })

同様の方法で、オブザーバブルを返すか、rxjsビー玉を使用することで、ジャスミンのspyOnPropertyメソッドを介してparamMapを直接テストできると思います。時間を節約できる可能性があり、追加のモッククラスを維持する必要もありません。それが有用であり、理にかなっていることを願っています。


余分なモックを維持するよりもはるかに優れており、テストでさまざまなパラメーターを簡単に設定できます。ありがとうございました!
migg

これは役に立ちます。さまざまなパラメータをスパイする方法を知っていますか:const dirName = this.route.snapshot.paramMap.get( 'dirName'); const actionType = this.route.snapshot.paramMap.get( 'actionType'); どのボットでspyOn(route.snapshot.paramMap、 'get')をスパイしますか?聞くキーを指定できますか?
speksy

上で述べたように、spyOnの代わりにspyOnPropertyを使用できると思います。たとえば、spyOnProperty(route.snapshot.paramMap.get、 'dirName')です。私があなたの質問に完全に答えていないならば、私に言うことを躊躇しないでください。ありがとう。
dimitris maf

10

これが私がAngular2.0最新でそれをテストした方法です...

import { ActivatedRoute, Data } from '@angular/router';

およびプロバイダーセクション

{
  provide: ActivatedRoute,
  useValue: {
    data: {
      subscribe: (fn: (value: Data) => void) => fn({
        yourData: 'yolo'
      })
    }
  }
}

プロバイダーセクションの完全なコードを提供できますか?
Michael JDI 2016年

これは完全なユニットテストクラスです。 plnkr.co/edit/UeCKnJ2sCCpLLQcWqEGX?p=catalogue
Rady

ngOnDestroyで登録解除をテストするにはどうすればよいですか
シヴァ

サブスクリプションを返さず、ngOnDestroyでcall .unsubscribe()を使用できないため、これは実際のユースケースでは機能しません。
Quovadisqc 2017

1
data:Observable.of({yourData: 'yolo'})は機能します。
Quovadisqc 2017

4

ActivatedRouteのモックを追加するだけです。

providers: [
  { provide: ActivatedRoute, useClass: MockActivatedRoute }
]

..。

class MockActivatedRoute {
  // here you can add your mock objects, like snapshot or parent or whatever
  // example:
  parent = {
    snapshot: {data: {title: 'myTitle ' } },
    routeConfig: { children: { filter: () => {} } }
  };
}

3

Angular> 5で作業している人の場合、Observable.of(); が機能していない場合は、 'rxjs'からimport {of}をインポートすることでof()だけを使用できます。


1

ルーティングパスのテストスイートを作成しているときに、次のような同じ問題が発生しました。

{
   path: 'edit/:property/:someId',
   component: YourComponent,
   resolve: {
       yourResolvedValue: YourResolver
   }
}

コンポーネントで、渡されたプロパティを次のように初期化しました。

ngOnInit(): void {    
   this.property = this.activatedRoute.snapshot.params.property;
   ...
}

テストを実行するときに、モックのActivatedRoute "useValue"でプロパティ値を渡さないと、 "fixture.detectChanges()"を使用して変更を検出するときに未定義になります。これは、ActivatedRouteのモック値にプロパティparams.propertyが含まれていないためです。次に、フィクスチャがコンポーネントの「this.property」を初期化するために、モックuseValueがこれらのパラメータを持っている必要があります。次のように追加できます。

  let fixture: ComponentFixture<YourComponent>;
  let component: YourComponent;
  let activatedRoute: ActivatedRoute; 

  beforeEach(done => {
        TestBed.configureTestingModule({
          declarations: [YourComponent],
          imports: [ YourImportedModules ],
          providers: [
            YourRequiredServices,
            {
              provide: ActivatedRoute,
              useValue: {
                snapshot: {
                  params: {
                    property: 'yourProperty',
                    someId: someId
                  },
                  data: {
                    yourResolvedValue: { data: mockResolvedData() }
                  }
                }
              }
            }
          ]
        })
          .compileComponents()
          .then(() => {
            fixture = TestBed.createComponent(YourComponent);
            component = fixture.debugElement.componentInstance;
            activatedRoute = TestBed.get(ActivatedRoute);
            fixture.detectChanges();
            done();
          });
      });

たとえば、次のようにテストを開始できます。

it('should ensure property param is yourProperty', async () => {
   expect(activatedRoute.snapshot.params.property).toEqual('yourProperty');
   ....
});

ここで、別のプロパティ値をテストしたいとします。次に、モックのActivatedRouteを次のように更新できます。

  it('should ensure property param is newProperty', async () => {
    activatedRoute.snapshot.params.property = 'newProperty';
    fixture = TestBed.createComponent(YourComponent);
    component = fixture.debugElement.componentInstance;
    activatedRoute = TestBed.get(ActivatedRoute);
    fixture.detectChanges();

    expect(activatedRoute.snapshot.params.property).toEqual('newProperty');
});

お役に立てれば!


0

テストクラスにプロバイダーを次のように追加しました。

{
  provide: ActivatedRoute,
  useValue: {
    paramMap: of({ get: v => { return { id: 123 }; } })
  } 
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.