Angular2 - 在测试中模拟 RouteParams

Posted

技术标签:

【中文标题】Angular2 - 在测试中模拟 RouteParams【英文标题】:Angular2 - mocking RouteParams in test 【发布时间】:2016-04-18 08:26:54 【问题描述】:

我在 Angular2 组件的测试中为 RouteParams 依赖项注入模拟时遇到了一些问题。我的一般想法是我可能缺少一些提供者。

测试失败:

无法解析“RouteParams”(?)的所有参数。确保所有参数都用 Inject 修饰 或具有有效的类型注释,并且“RouteParams”用 Injectable 装饰。

有谁知道可能是什么问题?

import 
  it,
  inject,
  injectAsync,
  describe,
  beforeEach,
  beforeEachProviders,
  TestComponentBuilder
 from 'angular2/testing';

import Component, provide from 'angular2/core';
import BaseRequestOptions, Http from 'angular2/http';
import MockBackend from 'angular2/http/testing';
import RouteParams, ROUTER_PROVIDERS, ROUTER_PRIMARY_COMPONENT from 'angular2/router';

// Load the implementations that should be tested
import Home from './home';
import Title from './providers/title';

describe('Home', () => 
  // provide our implementations or mocks to the dependency injector

  beforeEachProviders(() => [
    Title,
    Home,
    provide(RouteParams,  useValue: new RouteParams( id: '1' ) ),
    BaseRequestOptions,
    MockBackend,
    provide(Http, 
        useFactory: function(backend, defaultOptions) 
            return new Http(backend, defaultOptions);
        ,
        deps: [MockBackend, BaseRequestOptions]
    ),
    provide(RouteParams, 
        useFactory: function() 
            return new RouteParams( 'id':'1' );
        
    )
  ]);

  it('should have a title', inject([ Home ], (home) => 
    expect(home.title.value).toEqual('Angular 2');
  ));

  it('should have a http', inject([ Home ], (home) => 
    expect(!!home.http).toEqual(true);
  ));

  it('should log ngOnInit', inject([ Home ], (home) => 
    spyOn(console, 'log');
    spyOn(console, 'info');
    expect(console.log).not.toHaveBeenCalled();
    expect(console.info).not.toHaveBeenCalled();

    home.ngOnInit();
    expect(console.log).toHaveBeenCalled();
    expect(console.info).toHaveBeenCalledWith('1');
  ));

);

【问题讨论】:

【参考方案1】:

自己解决了这个问题,你使用 RouteProvider 来模拟

provide(RouteParams,  useValue: new RouteParams( id: '1' ) )
import 
  it,
  inject,
  injectAsync,
  describe,
  beforeEach,
  beforeEachProviders,
  TestComponentBuilder
 from 'angular2/testing';

import Component, provide from 'angular2/core';
import BaseRequestOptions, Http from 'angular2/http';
import MockBackend from 'angular2/http/testing';
import RouteParams, ROUTER_PROVIDERS, ROUTER_PRIMARY_COMPONENT from 'angular2/router';

// Load the implementations that should be tested
import Home from './home';
import Title from './providers/title';

describe('Home', () => 
  // provide our implementations or mocks to the dependency injector

  beforeEachProviders(() => [
    Title,
    Home,
    provide(RouteParams,  useValue: new RouteParams( id: '1' ) ),
    BaseRequestOptions,
    MockBackend,
    provide(Http, 
        useFactory: function(backend, defaultOptions) 
            return new Http(backend, defaultOptions);
        ,
        deps: [MockBackend, BaseRequestOptions]
    )
  ]);

  it('should have a title', inject([ Home ], (home) => 
    expect(home.title.value).toEqual('Angular 2');
  ));

  it('should have a http', inject([ Home ], (home) => 
    expect(!!home.http).toEqual(true);
  ));

  it('should log ngOnInit', inject([ Home ], (home) => 
    spyOn(console, 'log');
    spyOn(console, 'info');
    expect(console.log).not.toHaveBeenCalled();
    expect(console.info).not.toHaveBeenCalled();

    home.ngOnInit();
    expect(console.log).toHaveBeenCalled();
    expect(console.info).toHaveBeenCalledWith('1');
  ));

);

【讨论】:

【参考方案2】:

适用于即使使用 Angular4

也能来到这里的人

我只是继续创建了一个模拟。我想诀窍是模拟你需要的ActivatedRoute 的部分。对我来说,这样做了:

import ActivatedRoute, ParamMap from '@angular/router';

/**
 * Mocking the ActivatedRoute which is injected in the Component on creation.
 * This allows for easier testing as values can be set as needed.
 */
class MockActivatedRoute 
   paramMap = Observable.of(new Params());


/**
 * Bare bones implementation of ParamMap used in mock. Further tests can expand
 * on this implementation as needed.
 */
class Params implements ParamMap 
  keys: string[];

  private routes: [key: string]: string|null = 
    subject: 'foo',
    time: 'd-123-1',
    device: 'all',
    location: 'c-123'
  ;

  constructor() 
    this.keys = Object.keys(this.routes);
  

  has(name: string): boolean 
    throw new Error('Method not implemented.');
  
  get(name: string): string|null 
    return this.routes[name];
  
  getAll(name: string): string[] 
    throw new Error('Method not implemented.');
  

然后确保在您的测试模块中提供模拟服务而不是真实的ActivatedRoute

providers: [
  
    provide: ActivatedRoute,
    useValue: new MockActivatedRoute(),
  
]

为了完成,我在我正在测试的组件中使用它的方式:

ngOnInit() 
  this.route.paramMap
      .map((params: ParamMap) => params.get('subject') as string)
      .subscribe((subject: string) => this.subject = subject);

【讨论】:

以上是关于Angular2 - 在测试中模拟 RouteParams的主要内容,如果未能解决你的问题,请参考以下文章

Angular2(TypeScript)中的单元测试/模拟窗口属性

Angular 2 单元测试组件,模拟 ContentChildren

Angular 2:如何在单元测试时模拟 ChangeDetectorRef

如何在单元测试中模拟 AngularFire 2 服务?

Angular 2 fakeAsync 在使用tick()的函数中等待超时?

模拟服务以调用本地 json-server 而不是远程端点 - 测试语法? (角度 2)