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.

Colby Cox
sumber

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})
  }
}
zmanc
sumber
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!

Colby Cox
sumber
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.

dimitris maf
sumber
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'
      })
    }
  }
}
Rady
sumber
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: () => {} } }
  };
}
Francesco Borzi
sumber
3

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

Shashank Sharma
sumber
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!

Diego Herrera
sumber
0

Menambahkan penyedia di kelas pengujian sebagai:

{
  provide: ActivatedRoute,
  useValue: {
    paramMap: of({ get: v => { return { id: 123 }; } })
  } 
}
adelinor
sumber