How to unit test a component that depends on parameters from ActivatedRoute?

前端 未结 8 1105
离开以前
离开以前 2020-12-04 09:37

I am unit testing a component that is used to edit an object. The object has an unique id that is used in order to grab the specific object from an array of obj

相关标签:
8条回答
  • 2020-12-04 09:50

    In angular 8+ there is the RouterTestingModule, which you can use in order to have access to the ActivatedRoute or Router of the component. Also you can pass routes to the RouterTestingModule and create spies for the requested methods of route.

    For example in my component I have:

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

    And in my test I have:

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

    and later in the 'it' section:

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

    In a similar way, I think you can test directly the paramMap via the spyOnProperty method of jasmine, by returning an observable or using rxjs marbles. It might save some time & also it does not require to maintain an extra mock class. Hope that it is useful and it makes sense.

    0 讨论(0)
  • 2020-12-04 09:54

    Just add a mock of the 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: () => {} } }
      };
    }
    
    0 讨论(0)
  • 2020-12-04 09:56

    For some folks working on Angular > 5, if Observable.of(); is not working then they can use just of() by importing import { of } from 'rxjs';

    0 讨论(0)
  • 2020-12-04 09:58

    The simplest way to do this is to just use the useValue attribute and provide an Observable of the value you want to mock.

    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})
      }
    }
    
    0 讨论(0)
  • 2020-12-04 10:00

    Ran into the same issue while creating test suites for a routing path as:

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

    In the component, I initialized the passed property as:

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

    When running the tests, if you do not pass a property value in your mock ActivatedRoute "useValue", then you will get undefined when detecting changes using "fixture.detectChanges()". This is because the mock values for ActivatedRoute does not contain the property params.property. Then, it is required for the mock useValue to have those params in order for the fixture to initialize the 'this.property' in the component. You can add it as:

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

    The you can start testing as for example:

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

    Now, lets say you would like to test a different property value, then you can update your mock ActivatedRoute as:

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

    Hope this helps!

    0 讨论(0)
  • 2020-12-04 10:04

    Added provider in the test class as:

    {
      provide: ActivatedRoute,
      useValue: {
        paramMap: of({ get: v => { return { id: 123 }; } })
      } 
    }
    
    0 讨论(0)
提交回复
热议问题