Bagaimana cara menguji unit komponen yang bergantung pada parameter dari ActivatedRoute?


93

Saya menguji unit komponen yang digunakan untuk mengedit objek. Objek memiliki keunikan idyang digunakan untuk mengambil objek tertentu dari larik objek yang dihosting di layanan. Spesifik iddiperoleh melalui parameter yang diteruskan melalui perutean, khususnya melalui ActivatedRoutekelas.

Konstruktornya adalah sebagai berikut:

 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];

Saya ingin menjalankan pengujian unit dasar pada komponen ini. Namun, saya tidak yakin bagaimana saya dapat memasukkan idparameter, dan komponen membutuhkan parameter ini.

Ngomong-ngomong: Saya sudah memiliki olok-olok untuk Sessionlayanan tersebut, jadi jangan khawatir.

Jawaban:


135

Cara termudah untuk melakukan ini adalah dengan hanya menggunakan useValueatribut dan memberikan Observable dari nilai yang ingin Anda tiru.

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 tidak ada untuk saya! : S
Alejandro Sanz Díaz

4
Impor Dapat diamati dari rxjs / Observable
zmanc

6
Kode ini membuat kesalahan ini dalam proyek saya: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
MixerOID

5
RxJs 6 ofharus digunakan sendiri. Anda juga mungkin akan menggunakan RouterTestingModulealih-alih kode jawaban ini.
Ben Racicot

5
@BenRacicot jawaban ini diberikan sebelum RxJs 6 ada. Selain itu, dengan mengatakan "lakukan ini sebagai gantinya", berikan jawaban yang dapat diberi suara positif secara langsung.
zmanc

18

Saya telah menemukan cara untuk melakukan ini!

Karena ActivatedRoutelayanan, layanan tiruan untuk itu dapat didirikan. Sebut saja layanan tiruan ini MockActivatedRoute. Kami akan memperpanjang ActivatedRoutedi MockActivatedRoute, sebagai berikut:

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

Garis super(null, ....)menginisialisasi kelas super, yang memiliki empat parameter wajib. Namun, dalam hal ini, kami tidak memerlukan apa pun dari salah satu parameter ini, jadi kami menginisialisasinya ke nullnilai. Yang kita butuhkan hanyalah nilai paramsyang merupakan Observable<>. Oleh karena itu, dengan this.params, kita mengganti nilai dari paramsdan menginisialisasinya menjadi Observable<>parameter yang diandalkan oleh subjek uji.

Kemudian, seperti layanan tiruan lainnya, lakukan inisialisasi dan ganti penyedia komponen.

Semoga berhasil!


1
Saya menghadapi ini sekarang! Namun, saya mendapatkan kesalahan saat mencoba menggunakan superatau Observable. Dari mana asalnya?
Aarmora

super()dibuat. Observableberasal dari rxjs/Observableatau hanya rxjsbergantung pada versi Anda. Anda akan mendapatkannya menggunakan import {Observable} from 'rxjs'.
oooyaya

Anda telah menerima satu jawaban dan memposting yang lain ... jika ini adalah Highlander (dan hanya mungkin ada satu), mana yang "benar-benar" Anda pilih dan mengapa? Artinya, saya pikir ini pada dasarnya direduksi menjadi hal yang sama dengan jawaban zmanc, yang Anda terima. Apakah Anda menemukan nilai tambahan dari menyiapkan tiruan yang [sedikit] lebih rumit ini?
ruffin

11

Di angular 8+ ada RouterTestingModule, yang dapat Anda gunakan untuk memiliki akses ke ActivatedRoute atau Router komponen. Anda juga dapat meneruskan rute ke RouterTestingModule dan membuat mata-mata untuk metode rute yang diminta.

Misalnya di komponen saya, saya punya:

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

Dan dalam pengujian saya, saya memiliki:

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

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

dan kemudian di bagian 'itu':

  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
  })

Dengan cara yang sama, saya pikir Anda dapat menguji langsung paramMap melalui metode spyOnProperty melati, dengan mengembalikan kelereng yang dapat diamati atau menggunakan kelereng rxjs. Ini mungkin menghemat waktu & juga tidak perlu mempertahankan kelas tiruan tambahan. Semoga bermanfaat dan masuk akal.


Jauh lebih baik daripada harus mempertahankan tiruan ekstra dan Anda dapat dengan mudah mengatur parameter berbeda dalam pengujian. Terima kasih!
migg

Ini membantu. Apakah Anda tahu cara memata-matai parameter yang berbeda: const dirName = this.route.snapshot.paramMap.get ('dirName'); const actionType = this.route.snapshot.paramMap.get ('actionType'); Di mana bot akan memata-matai (route.snapshot.paramMap, 'get')? Dapatkah saya menentukan kunci untuk mendengarkan?
speksy

Seperti yang saya sebutkan di atas, saya rasa Anda bisa menggunakan spyOnProperty daripada spyOn, misalnya spyOnProperty (route.snapshot.paramMap.get, 'dirName'). Jika saya belum menjawab pertanyaan Anda sepenuhnya, jangan ragu untuk memberi tahu saya. Terima kasih.
dimitris maf

10

Inilah cara saya mengujinya di angular 2.0 terbaru ...

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

dan di bagian Penyedia

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

Bisakah Anda memberikan kode lengkap untuk bagian penyedia?
Michael JDI

Ini adalah kelas pengujian unit lengkap. plnkr.co/edit/UeCKnJ2sCCpLLQcWqEGX?p=catalogue
Rady

Bagaimana Anda menguji berhenti berlangganan di ngOnDestroy
shiva

Ini akan rusak dalam kasus penggunaan kehidupan nyata karena Anda tidak mengembalikan langganan dan Anda tidak akan bisa menggunakan panggilan .unsubscribe () di ngOnDestroy.
Quovadisqc

1
data: Observable.of ({yourData: 'yolo'}) akan berfungsi.
Quovadisqc

4

Cukup tambahkan tiruan dari 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

Untuk beberapa orang yang mengerjakan Angular> 5, jika Observable.of (); tidak bekerja maka mereka dapat menggunakan hanya () dengan mengimpor import {of} dari 'rxjs';


1

Mengalami masalah yang sama saat membuat rangkaian pengujian untuk jalur perutean sebagai:

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

Di dalam komponen, saya menginisialisasi properti yang diteruskan sebagai:

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

Saat menjalankan pengujian, jika Anda tidak meneruskan nilai properti di ActivatedRoute tiruan "useValue", Anda akan mendapatkan tidak terdefinisi saat mendeteksi perubahan menggunakan "fixture.detectChanges ()". Ini karena nilai tiruan untuk ActivatedRoute tidak berisi properti params.property. Kemudian, ini diperlukan untuk useValue tiruan untuk memiliki params tersebut agar fixture dapat menginisialisasi 'this.property' dalam komponen. Anda dapat menambahkannya sebagai:

  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();
          });
      });

Anda dapat memulai pengujian sebagai contoh:

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

Sekarang, katakanlah Anda ingin menguji nilai properti yang berbeda, lalu Anda dapat memperbarui ActivatedRoute tiruan Anda sebagai:

  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');
});

Semoga ini membantu!


0

Menambahkan penyedia di kelas pengujian sebagai:

{
  provide: ActivatedRoute,
  useValue: {
    paramMap: of({ get: v => { return { id: 123 }; } })
  } 
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.