จะทดสอบส่วนประกอบที่ขึ้นอยู่กับพารามิเตอร์จาก 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แอตทริบิวต์และระบุค่าที่สังเกตได้ที่คุณต้องการเยาะเย้ย

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
Alejandro Sanz Díaz

4
นำเข้า Observable จาก rxjs / 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, ....)เริ่มต้นซุปเปอร์คลาสซึ่งมีพารามิเตอร์บังคับสี่ตัว อย่างไรก็ตามในกรณีนี้เราไม่ต้องการอะไรจากพารามิเตอร์เหล่านี้ดังนั้นเราจึงเริ่มต้นให้เป็น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+ มี RouterTestingModule ซึ่งคุณสามารถใช้เพื่อเข้าถึง ActivatedRoute หรือเราเตอร์ของส่วนประกอบ นอกจากนี้คุณยังสามารถส่งเส้นทางไปยัง 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('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
  })

ในทำนองเดียวกันฉันคิดว่าคุณสามารถทดสอบ paramMap โดยตรงผ่านวิธี spyOnProperty ของจัสมินโดยส่งคืนที่สังเกตได้หรือใช้หินอ่อน rxjs อาจช่วยประหยัดเวลาได้บ้างและไม่จำเป็นต้องมีชั้นเรียนจำลองเพิ่มเติม หวังว่าจะเป็นประโยชน์และเข้าท่า


ดีกว่าการต้องดูแลการเยาะเย้ยเพิ่มเติมและคุณสามารถตั้งค่าพารามิเตอร์ต่างๆในการทดสอบได้อย่างง่ายดาย ขอบคุณ!
migg

สิ่งนี้ช่วยได้ คุณรู้วิธีการสอดแนมพารามิเตอร์ต่าง ๆ หรือไม่: const dirName = this.route.snapshot.paramMap.get ('dirName'); const actionType = this.route.snapshot.paramMap.get ('actionType'); บอทใดจะสอดแนม spyOn (route.snapshot.paramMap, 'get') ฉันสามารถระบุคีย์ที่จะฟังได้หรือไม่?
Speksy

ดังที่ฉันได้กล่าวไว้ข้างต้นฉันคิดว่าคุณสามารถใช้ spyOnProperty แทน spyOn ได้เช่น 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

สิ่งนี้จะทำลายกรณีการใช้งานในชีวิตจริงเนื่องจากคุณไม่ได้คืนการสมัครรับข้อมูลและคุณจะไม่สามารถใช้การโทร .unsubscribe () ใน ngOnDestroy ได้
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 (); ไม่ทำงานพวกเขาสามารถใช้เพียงแค่ () โดยการนำเข้าการนำเข้า {of} จาก 'rxjs';


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 จากนั้นจึงจำเป็นสำหรับการจำลอง useValue ที่จะต้องมีพารามิเตอร์เหล่านั้นเพื่อให้ฟิกซ์เจอร์เริ่มต้น 'this.property' ในคอมโพเนนต์ คุณสามารถเพิ่มเป็น:

  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.