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
알레한드로 산즈 디아즈

4
rxjs / Observable에서 Observable 가져 오기
zmanc

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
MixerOID

5
RxJs 6 of은 단독으로 사용해야합니다. 또한 RouterTestingModule이 답변의 코드 대신 사용할 가능성이 있습니다 .
Ben Racicot

5
@BenRacicot이 대답은 RxJs 6이 존재하기 전에 주어졌습니다. 또한 "대신이 작업을 수행하십시오"라고 말하면 직접 찬성 할 수있는 답변을 제공합니다.
zmanc

18

나는 이것을하는 방법을 알아 냈다!

ActivatedRoute서비스 이므로 모의 서비스를 설정할 수 있습니다. 이것을 모의 서비스라고합시다 MockActivatedRoute. ActivatedRoute에서 MockActivatedRoute다음과 같이 확장 합니다.

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

라인 super(null, ....)은 4 개의 필수 매개 변수가있는 수퍼 클래스를 초기화합니다. 그러나이 경우에는 이러한 매개 변수에서 아무것도 필요하지 않으므로 null값으로 초기화합니다 . 모든 우리의 필요의 값입니다 params이다 Observable<>. 따라서를 사용 this.params하여의 값을 재정의 하고 테스트 대상이 의존하는 매개 변수 params가되도록 초기화합니다 Observable<>.

그런 다음 다른 모의 서비스와 마찬가지로 초기화하고 구성 요소의 공급자를 재정의합니다.

행운을 빕니다!


1
나는 지금 이것을 직면하고있다! 그러나 super또는 을 사용하려고하면 오류가 발생합니다 Observable. 이것들은 어디에서 왔습니까?
Aarmora

super()내장된다. Observable에서입니다 rxjs/Observable단지 나 rxjs버전에 따라. 당신은 그것을 사용하여 얻을 것 import {Observable} from 'rxjs'입니다.
oooyaya

하나의 답변을 수락하고 다른 답변을 게시했습니다. 이것이 Highlander 인 경우 (하나만있을 수 있음), 어떤 답변을 "정말"선택했고 그 이유는 무엇입니까? 즉, 이것은 본질적으로 당신이 받아 들인 zmanc의 대답과 동일한 것으로 감소한다고 생각합니다. 이 [약간] 더 복잡한 모의를 설정하여 추가적인 가치를 찾았습니까?
ruffin

11

각도 8+에는 구성 요소의 ActivatedRoute 또는 라우터에 액세스하기 위해 사용할 수있는 RouterTestingModule이 있습니다. 또한 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
  })

비슷한 방식으로 jasmine의 spyOnProperty 메서드를 통해 관찰 가능 항목을 반환하거나 rxjs 구슬을 사용하여 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

angular 2.0 최신 버전에서 테스트 한 방법은 다음과 같습니다.

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

및 공급자 섹션

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

제공자 섹션에 대한 완전한 코드를 제공 할 수 있습니까?
Michael JDI

이것은 완전한 단위 테스트 클래스입니다. plnkr.co/edit/UeCKnJ2sCCpLLQcWqEGX?p=catalogue
Rady

ngOnDestroy에서 구독 취소를 어떻게 테스트합니까?
shiva

구독을 반환하지 않고 ngOnDestroy에서 call .unsubscribe ()를 사용할 수 없기 때문에 실제 사용 사례에서 중단됩니다.
Quovadisqc

1
data : Observable.of ({yourData : 'yolo'})는 작동합니다.
Quovadisqc

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.