Angular 如何模拟ActivatedRoute

Angular 如何模拟ActivatedRoute,angular,unit-testing,angular-router,Angular,Unit Testing,Angular Router,我正在学习角度,我想做测试,但我被卡住了。我有一个函数: ngOnInit(): void { this.route.paramMap .switchMap((params: ParamMap) => this.SomethingService.getSomething(params.get('id'))) .subscribe(something => { this.something = something

我正在学习角度,我想做测试,但我被卡住了。我有一个函数:

ngOnInit(): void {
    this.route.paramMap
    .switchMap((params: ParamMap) => 
        this.SomethingService.getSomething(params.get('id')))
        .subscribe(something => {
            this.something = something;
            this.doSomethingElse();
    });
}
在哪里

route: ActivatedRoute

我想测试它,但我不知道如何模拟ActivatedRoute

模拟
ActivatedRoute
的一个简单方法是:

    TestBed.configureTestingModule({
      declarations: [YourComponenToTest],
      providers: [
        {
          provide: ActivatedRoute,
          useValue: {
            params: Observable.from([{id: 1}]),
          },
        },
      ]
    });
然后在您的测试中,它将是可用的,并且您的函数应该使用它(至少是ActivatedRoute部分)

如果要将它存储在变量中,可以使用
TestBed.get(ActivatedRoute)
it
函数中获取它


别忘了从
rxjs/Rx
导入可观测数据,而不是从
rxjs/Observable
导入可观测数据。我在使用
paramMap
而不是
params
时遇到了同样的问题。这让它对我起了作用,至少在最简单的情况下:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Observable } from 'rxjs';
import 'rxjs/add/observable/of';
import { ComponentToTest } from './component-to-test.component';
import { ActivatedRoute } from '@angular/router';

TestBed.configureTestingModule({
  declarations: [ComponentToTest],
  providers: [
    { 
        provide: ActivatedRoute, 
        useValue: {
            paramMap: Observable.of({ get: (key) => 'value' })
        }
    }
  ]
});

对于任何对如何正确使用多个属性感兴趣的人,以下是定义模拟类的方法:

import { convertToParamMap } from '@angular/router';
import { Observable } from 'rxjs/Observable';

export class ActivatedRouteMock {
    public paramMap = Observable.of(convertToParamMap({ 
        testId: 'abc123',
        anotherId: 'd31e8b48-7309-4c83-9884-4142efdf7271',          
    }));
}

通过这种方式,您可以订阅您的
paramMap
并检索多个值-在这种情况下,
testId
anotherId

修改上述Andrus的答案

对于RxJS 6+:

import { convertToParamMap } from '@angular/router';
import { of } from 'rxjs';


export class ActivatedRouteMock {
    public paramMap = of(convertToParamMap({ 
        testId: 'abc123',
        anotherId: 'd31e8b48-7309-4c83-9884-4142efdf7271',          
    }));
}

在我的例子中,我必须创建一个新类来处理这种类型的测试,这个类将允许您处理快照、查询参数和参数

import { Params } from '@angular/router';
import { BehaviorSubject } from 'rxjs';

export class MockActivatedRoute {
  private innerTestParams?: any;
  private subject?: BehaviorSubject<any> = new BehaviorSubject(this.testParams);

  params = this.subject.asObservable();
  queryParams = this.subject.asObservable();

  constructor(params?: Params) {
    if (params) {
      this.testParams = params;
    } else {
      this.testParams = {};
    }
  }

  get testParams() {
    return this.innerTestParams;
  }

  set testParams(params: {}) {
    this.innerTestParams = params;
    this.subject.next(params);
  }

  get snapshot() {
    return { params: this.testParams, queryParams: this.testParams };
  }
}
从'@angular/router'导入{Params};
从“rxjs”导入{BehaviorSubject};
导出类MockActivatedRoute{
私有innerTestParams?:任意;

private subject?:BehaviorSubject

在最近的Angular版本中,项目的
aot
设置在默认情况下是打开的(以便更好地进行编译时类型检查)。如果项目是这样,那么您可能至少需要删除ActivatedRoute和ActivatedRouteSnapshot的所有属性。类似于以下内容:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Type } from '@angular/core';
import { Location } from '@angular/common';
import { MockPlatformLocation } from '@angular/common/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ActivatedRoute, ActivatedRouteSnapshot, Params, ParamMap, convertToParamMap } from '@angular/router';
import { of, BehaviorSubject } from 'rxjs';

import { HeroDetailComponent } from './hero-detail.component';
import { Hero } from '../hero';


export class MockActivatedRouteSnapshot implements ActivatedRouteSnapshot {
  private innerTestParams?: Params;

  constructor(params?: Params) {
    if (params) {
      this.testParams = params;
    } else {
      this.testParams = null;
    }
  }

  private get testParams() {
    return this.innerTestParams;
  }

  private set testParams(params: Params) {
    this.innerTestParams = params;
  }

  get paramMap() {
    return convertToParamMap(this.testParams);
  }

  get queryParamMap() {
    return this.paramMap;
  }

  get url() {
    return null;
  }

  get fragment() {
    return null;
  }

  get data() {
    return null;
  }

  get outlet() {
    return null;
  }

  get params() {
    return this.innerTestParams;
  }

  get queryParams() {
    return this.innerTestParams;
  }

  get component() {
    return null;
  }

  get routeConfig() {
    return null;
  }

  get root() {
    return null;
  }

  get parent() {
    return null;
  }

  get firstChild() {
    return null;
  }

  get children() {
    return null;
  }

  get pathFromRoot() {
    return null;
  }
}


export class MockActivatedRoute implements ActivatedRoute {
  private innerTestParams?: Params;
  private subject?: BehaviorSubject<Params> = new BehaviorSubject(this.testParams);
  private paramMapSubject?: BehaviorSubject<ParamMap> = new BehaviorSubject(convertToParamMap(this.testParams));

  constructor(params?: Params) {
    if (params) {
      this.testParams = params;
    } else {
      this.testParams = null;
    }
  }

  private get testParams() {
    return this.innerTestParams;
  }

  private set testParams(params: Params) {
    this.innerTestParams = params;
    this.subject.next(params);
    this.paramMapSubject.next(convertToParamMap(params));
  }

  get snapshot() {
    return new MockActivatedRouteSnapshot(this.testParams);
  }

  get params() {
    return this.subject.asObservable();
  }

  get queryParams() {
    return this.params;
  }

  get paramMap() {
    return this.paramMapSubject.asObservable();
  }

  get queryParamMap() {
    return this.paramMap;
  }

  get url() {
    return null;
  }

  get fragment() {
    return null;
  }

  get data() {
    return null;
  }

  get outlet() {
    return null;
  }

  get component() {
    return null;
  }

  get routeConfig() {
    return null;
  }

  get root() {
    return null;
  }

  get parent() {
    return null;
  }

  get firstChild() {
    return null;
  }

  get children() {
    return null;
  }

  get pathFromRoot() {
    return null;
  }
}


describe('HeroDetailComponent', () => {
  let component: HeroDetailComponent;
  let fixture: ComponentFixture<HeroDetailComponent>;
  let httpMock: HttpTestingController;
  let routeMock: MockActivatedRoute;
  let initialMockParams: Params;
  let locationMock: MockPlatformLocation;

  beforeEach(async(() => {
    initialMockParams = {id: 11};
    routeMock = new MockActivatedRoute(initialMockParams);
    locationMock = new MockPlatformLocation;
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule
      ],
      declarations: [ HeroDetailComponent ],
      providers: [
        {
          provide: ActivatedRoute, useValue: routeMock,
        },
        {
          provide: Location, useValue: locationMock,
        }
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HeroDetailComponent);
    component = fixture.componentInstance;
    httpMock = TestBed.inject<HttpTestingController>(HttpTestingController as Type<HttpTestingController>);
    fixture.detectChanges();
  });

  afterEach(() => {
    httpMock.verify();
  });

  it('should be created', () => {
    fixture.detectChanges();

    expect(component).toBeTruthy();

    const dummyHero: Hero = { id: 11, name: 'dummyHero' };
    const req = httpMock.expectOne('api/details/11');
    req.flush(dummyHero);
  });
});
从'@angular/core/testing'导入{async,ComponentFixture,TestBed};
从'@angular/core'导入{Type};
从“@angular/common”导入{Location};
从“@angular/common/testing”导入{MockPlatformLocation};
从“@angular/common/http/testing”导入{HttpClientTestingModule,HttpTestingController};
从“@angular/router”导入{ActivatedRoute,ActivatedRouteSnapshot,Params,ParamMap,convertToParamMap};
从'rxjs'导入{of,BehaviorSubject};
从“./hero detail.component”导入{HeroDetailComponent};
从“../Hero”导入{Hero};
导出类MockActivatedRouteSnapshot实现ActivatedRouteSnapshot{
私有innerTestParams?:参数;
构造函数(参数?:参数){
如果(参数){
this.testParams=params;
}否则{
this.testParams=null;
}
}
private get testParams(){
返回this.innerTestParams;
}
私有集testParams(params:params){
this.innerTestParams=params;
}
获取参数映射(){
返回convertToParamMap(this.testParams);
}
获取queryParamMap(){
返回此.paramMap;
}
获取url(){
返回null;
}
获取片段(){
返回null;
}
获取数据(){
返回null;
}
获取出口(){
返回null;
}
获取参数(){
返回this.innerTestParams;
}
获取查询参数(){
返回this.innerTestParams;
}
获取组件(){
返回null;
}
获取routeConfig(){
返回null;
}
获取根(){
返回null;
}
获取父对象(){
返回null;
}
获取第一个孩子(){
返回null;
}
得到孩子{
返回null;
}
获取路径fromRoot(){
返回null;
}
}
导出类MockActivateRoute实现ActivateRoute{
私有innerTestParams?:参数;
私有主题?:BehaviorSubject=新的BehaviorSubject(this.testParams);
private paramMapSubject?:BehaviorSubject=新的BehaviorSubject(convertToParamMap(this.testParams));
构造函数(参数?:参数){
如果(参数){
this.testParams=params;
}否则{
this.testParams=null;
}
}
private get testParams(){
返回this.innerTestParams;
}
私有集testParams(params:params){
this.innerTestParams=params;
this.subject.next(params);
this.paramMapSubject.next(convertToParamMap(params));
}
获取快照(){
返回新的MockActivatedRouteSnapshot(this.testParams);
}
获取参数(){
返回此.subject.asObservable();
}
获取查询参数(){
返回此.params;
}
获取参数映射(){
返回此.parammappubject.asObservable();
}
获取queryParamMap(){
返回此.paramMap;
}
获取url(){
返回null;
}
获取片段(){
返回null;
}
获取数据(){
返回null;
}
获取出口(){
返回null;
}
获取组件(){
返回null;
}
获取routeConfig(){
返回null;
}
获取根(){
返回null;
}
获取父对象(){
返回null;
}
获取第一个孩子(){
返回null;
}
得到孩子{
返回null;
}
获取路径fromRoot(){
返回null;
}
}
描述('HeroDetailComponent',()=>{
let组件:HeroDetailComponent;
let夹具:组件夹具;
让httpock:HttpTestingController;
让routeMock:MockActivatedRoute;
让initialMockParams:Params;
let locationMock:MockPlatformLocation;
beforeach(异步(()=>{
initialMockParams={id:11};
routeMock=新的MockActivatedRoute(初始MockParams);
locationMock=新的MockPlatformLocation;
TestBed.configureTestingModule({
进口:[
HttpClientTestingModule
],
声明:[HeroDetailComponent],
供应商:[
{
提供:ActivatedRoute,使用值:routeMock,
},
{
提供:位置,使用值:locationMock,
}
]
})
.compileComponents();
}));
在每个之前(()=>{
fixture=TestBed.createComponent(HeroDetailComponent);
组件=fixture.componentInstance;
httpMock=TestBed.inject(类型为HttpTestingController);
fixture.detectChanges();
});
之后(()=>{
httpMock.verify();
});
它('应该创建',()=>{
fixture.detectChanges();
expect(component.toBeTruthy();
常数
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Type } from '@angular/core';
import { Location } from '@angular/common';
import { MockPlatformLocation } from '@angular/common/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ActivatedRoute, ActivatedRouteSnapshot, Params, ParamMap, convertToParamMap } from '@angular/router';
import { of, BehaviorSubject } from 'rxjs';

import { HeroDetailComponent } from './hero-detail.component';
import { Hero } from '../hero';


export class MockActivatedRouteSnapshot implements ActivatedRouteSnapshot {
  private innerTestParams?: Params;

  constructor(params?: Params) {
    if (params) {
      this.testParams = params;
    } else {
      this.testParams = null;
    }
  }

  private get testParams() {
    return this.innerTestParams;
  }

  private set testParams(params: Params) {
    this.innerTestParams = params;
  }

  get paramMap() {
    return convertToParamMap(this.testParams);
  }

  get queryParamMap() {
    return this.paramMap;
  }

  get url() {
    return null;
  }

  get fragment() {
    return null;
  }

  get data() {
    return null;
  }

  get outlet() {
    return null;
  }

  get params() {
    return this.innerTestParams;
  }

  get queryParams() {
    return this.innerTestParams;
  }

  get component() {
    return null;
  }

  get routeConfig() {
    return null;
  }

  get root() {
    return null;
  }

  get parent() {
    return null;
  }

  get firstChild() {
    return null;
  }

  get children() {
    return null;
  }

  get pathFromRoot() {
    return null;
  }
}


export class MockActivatedRoute implements ActivatedRoute {
  private innerTestParams?: Params;
  private subject?: BehaviorSubject<Params> = new BehaviorSubject(this.testParams);
  private paramMapSubject?: BehaviorSubject<ParamMap> = new BehaviorSubject(convertToParamMap(this.testParams));

  constructor(params?: Params) {
    if (params) {
      this.testParams = params;
    } else {
      this.testParams = null;
    }
  }

  private get testParams() {
    return this.innerTestParams;
  }

  private set testParams(params: Params) {
    this.innerTestParams = params;
    this.subject.next(params);
    this.paramMapSubject.next(convertToParamMap(params));
  }

  get snapshot() {
    return new MockActivatedRouteSnapshot(this.testParams);
  }

  get params() {
    return this.subject.asObservable();
  }

  get queryParams() {
    return this.params;
  }

  get paramMap() {
    return this.paramMapSubject.asObservable();
  }

  get queryParamMap() {
    return this.paramMap;
  }

  get url() {
    return null;
  }

  get fragment() {
    return null;
  }

  get data() {
    return null;
  }

  get outlet() {
    return null;
  }

  get component() {
    return null;
  }

  get routeConfig() {
    return null;
  }

  get root() {
    return null;
  }

  get parent() {
    return null;
  }

  get firstChild() {
    return null;
  }

  get children() {
    return null;
  }

  get pathFromRoot() {
    return null;
  }
}


describe('HeroDetailComponent', () => {
  let component: HeroDetailComponent;
  let fixture: ComponentFixture<HeroDetailComponent>;
  let httpMock: HttpTestingController;
  let routeMock: MockActivatedRoute;
  let initialMockParams: Params;
  let locationMock: MockPlatformLocation;

  beforeEach(async(() => {
    initialMockParams = {id: 11};
    routeMock = new MockActivatedRoute(initialMockParams);
    locationMock = new MockPlatformLocation;
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule
      ],
      declarations: [ HeroDetailComponent ],
      providers: [
        {
          provide: ActivatedRoute, useValue: routeMock,
        },
        {
          provide: Location, useValue: locationMock,
        }
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HeroDetailComponent);
    component = fixture.componentInstance;
    httpMock = TestBed.inject<HttpTestingController>(HttpTestingController as Type<HttpTestingController>);
    fixture.detectChanges();
  });

  afterEach(() => {
    httpMock.verify();
  });

  it('should be created', () => {
    fixture.detectChanges();

    expect(component).toBeTruthy();

    const dummyHero: Hero = { id: 11, name: 'dummyHero' };
    const req = httpMock.expectOne('api/details/11');
    req.flush(dummyHero);
  });
});
providers: [{ provide: ActivatedRoute, useValue: { paramMap: new Subject() } }]
activatedRoute = TestBed.inject(ActivatedRoute);
activatedRoute.paramMap.next({ get: (key: string) => 'value1'});