ActivatedRoute parametresine bağlı bir bileşen nasıl birim test edilir?


93

Bir nesneyi düzenlemek için kullanılan bir bileşeni birim test ediyorum. Nesnenin, idbelirli bir nesneyi bir hizmette barındırılan nesneler dizisinden almak için kullanılan benzersiz bir özelliği vardır. Spesifik id, özellikle ActivatedRoutesınıf aracılığıyla yönlendirme yoluyla geçen bir parametre aracılığıyla sağlanır .

Yapıcı aşağıdaki gibidir:

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

Bu bileşen üzerinde temel birim testleri çalıştırmak istiyorum. Ancak, idparametreyi nasıl enjekte edebileceğimden emin değilim ve bileşenin bu parametreye ihtiyacı var .

Bu arada: SessionHizmet için zaten bir taklidim var , bu yüzden endişelenmenize gerek yok.

Yanıtlar:


135

Bunu yapmanın en basit yolu, sadece useValueözniteliği kullanmak ve alay etmek istediğiniz değerin bir Gözlemlenebilirliğini sağlamaktır.

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 benim için yok! : S
Alejandro Sanz Díaz

4
Rxjs / Observable'dan İçe Aktarma Gözlemlenebilir
zmanc

6
Bu kod 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
projemde

5
RxJs 6 oftek başına kullanılmalıdır. Ayrıca RouterTestingModulebu cevabın kodu yerine muhtemelen kullanacaksınız .
Ben Racicot

5
@BenRacicot bu cevap RxJs 6 var olmadan önce verildi. Ayrıca bunun yerine "bunun yerine bunu yap" demek, doğrudan olumlu oylanabilecek bir yanıt verin.
zmanc

18

Bunu nasıl yapacağımı buldum!

Yana ActivatedRoutebir hizmettir, bunun için sahte bir hizmet kurulabilir. Bu sahte servisi arayalım MockActivatedRoute. Biz uzatacaktır ActivatedRouteiçinde MockActivatedRouteaşağıdaki gibi:

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

Satır super(null, ....), dört zorunlu parametresi olan süper sınıfı başlatır. Bununla birlikte, bu durumda, bu parametrelerin herhangi birinden hiçbir şeye ihtiyacımız yok, bu yüzden onları nulldeğerlere ilklendiriyoruz . İhtiyacımız paramsolan tek şey değeri bir Observable<>. Bu nedenle, birlikte this.paramsbiz değerini geçersiz paramsve olması başlatmak Observable<>Denek güvenerek olduğu parametrenin.

Ardından, diğer herhangi bir sahte hizmet gibi, onu başlatın ve bileşen için sağlayıcıyı geçersiz kılın.

İyi şanslar!


1
Şu anda bununla yüzleşiyorum! Ancak, superveya kullanmaya çalıştığımda hatalar alıyorum Observable. Bunlar nereden geliyor?
Aarmora

super()inşa edilmiştir. Observabledan rxjs/Observablesadece ya rxjssürümünüze bağlı. Kullanırsın import {Observable} from 'rxjs'.
oooyaya

Bir cevabı kabul ettiniz ve bir başkasını gönderdiniz ... Eğer bu İskoçyalı ise (ve sadece bir tane olabilirdi), hangisini "gerçekten" seçtiniz ve neden? Yani, bence bu aslında sizin kabul ettiğiniz zmanc'ın cevabıyla aynı şeye indirgeniyor. Bu [biraz] daha karmaşık taklidi kurmaktan ek bir değer buldunuz mu?
2019

11

Açısal 8+ sürümünde, bileşenin ActivatedRoute veya Router'ına erişim sağlamak için kullanabileceğiniz RouterTestingModule vardır. Ayrıca RouterTestingModule'a rotalar aktarabilir ve istenen rota yöntemleri için casuslar oluşturabilirsiniz.

Örneğin bileşenimde:

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

Ve testimde var:

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

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

ve daha sonra "o" bölümünde:

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

Benzer şekilde, paramMap'i yaseminin spyOnProperty yöntemi ile, gözlemlenebilir bir öğe döndürerek veya rxjs mermerleri kullanarak doğrudan test edebileceğinizi düşünüyorum. Biraz zaman kazandırabilir ve ayrıca fazladan bir alay dersi gerektirmez. Umarım yararlıdır ve mantıklıdır.


Fazladan bir alay yapmak zorunda kalmaktan çok daha iyi ve testlerde farklı parametreleri kolayca ayarlayabilirsiniz. Teşekkür ederim!
migg

Bu yardımcı olur. Farklı parametreler üzerinde nasıl casusluk yapılacağını biliyor musunuz: const dirName = this.route.snapshot.paramMap.get ('dirName'); const actionType = this.route.snapshot.paramMap.get ('actionType'); SpyOn'u hangi botta casusluk yapacak (route.snapshot.paramMap, 'get')? Dinlemek için anahtar belirtebilir miyim?
speksy

Yukarıda bahsettiğim gibi spyOn yerine spyOnProperty kullanabileceğinizi düşünüyorum, örneğin spyOnProperty (route.snapshot.paramMap.get, 'dirName'). Sorunuzu tam olarak cevaplamadıysam, bana söylemekten çekinmeyin. Teşekkürler.
dimitris maf

10

İşte en son açısal 2.0'da nasıl test ettim ...

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

ve Sağlayıcılar bölümünde

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

Sağlayıcılar bölümü için eksiksiz kod sağlayabilir misiniz?
Michael JDI

Bu tam bir birim test sınıfıdır. plnkr.co/edit/UeCKnJ2sCCpLLQcWqEGX?p=catalogue
Rady

NgOnDestroy'da abonelikten çıkmayı nasıl test edersiniz
shiva

Bu, gerçek hayattaki bir kullanım durumunda bozulacaktır çünkü bir aboneliği iade etmiyorsunuz ve ngOnDestroy'da call .unsubscribe () kullanamayacaksınız.
Quovadisqc

1
data: Observable.of ({yourData: 'yolo'}) işe yarardı.
Quovadisqc

4

ActivatedRoute'un bir taklidini ekleyin:

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 üzerinde çalışan bazı kişiler için Observable.of (); çalışmıyorsa sadece () 'yi' rxjs'den içe aktararak {of} kullanabilir;


1

Bir yönlendirme yolu için test paketleri oluştururken aşağıdaki sorunla aynı sorunla karşılaştık:

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

Bileşende, iletilen özelliği şu şekilde başlattım:

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

Testleri çalıştırırken, ActivatedRoute adlı sahte "useValue" nizde bir özellik değeri geçirmezseniz, "fixture.detectChanges ()" kullanarak değişiklikleri tespit ederken tanımsız olursunuz. Bunun nedeni, ActivatedRoute için sahte değerlerin params.property özelliğini içermemesidir. Ardından, fikstürün bileşendeki "this.property" öğesini başlatması için sahte useValue parametresinin bu parametrelere sahip olması gerekir. Bunu şu şekilde ekleyebilirsiniz:

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

Örneğin test etmeye başlayabilirsiniz:

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

Şimdi, farklı bir mülk değerini test etmek istediğinizi varsayalım, ardından sahte ActivatedRoute'unuzu şu şekilde güncelleyebilirsiniz:

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

Bu yardımcı olur umarım!


0

Test sınıfına şu şekilde sağlayıcı eklendi:

{
  provide: ActivatedRoute,
  useValue: {
    paramMap: of({ get: v => { return { id: 123 }; } })
  } 
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.