如何对依赖ActivatedRoute的参数的组件进行单元测试?


92

我正在对用于编辑对象的组件进行单元测试。该对象具有唯一性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服务进行了模拟,因此不用担心。

Answers:


134

最简单的方法是仅使用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
Alejandro SanzDíaz'16

4
从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
导致

5
RxJs 6of应该单独使用。另外,您可能会使用RouterTestingModule该答案的代码。
本·拉西科特

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答案。通过设置此[稍微]更复杂的模拟程序,您是否发现了其他价值?
鲁芬

11

在angular 8+中,提供了RouterTestingModule,您可以使用该模块来访问组件的ActivatedRoute或Router。您也可以将路由传递到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
  })

以类似的方式,我认为您可以通过返回可观察的结果或使用rxjs大理石通过茉莉花的spyOnProperty方法直接测试paramMap。这可能会节省一些时间,也不需要维护额外的模拟类。希望它有用并且有意义。


比必须维护一个额外的模拟要好得多,您可以轻松地在测试中设置不同的参数。谢谢!
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

这将打破现实生活中的用例,因为您没有返回订阅,并且将无法在ngOnDestroy中使用call .unsubscribe()。
Quovadisqc

1
资料: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: () => {} } }
  };
}


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 }; } })
  } 
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.