我正在学习Angular而且我想做测试,但是我被困了。我有一个功能:
ngOnInit(): void {
this.route.paramMap
.switchMap((params: ParamMap) =>
this.SomethingService.getSomething(params.get('id')))
.subscribe(something => {
this.something = something;
this.doSomethingElse();
});
}
其中
route: ActivatedRoute
我想测试它,但我不知道如何模拟ActivatedRoute
答案 0 :(得分:13)
模拟ActivatedRoute的一种简单方法是:
TestBed.configureTestingModule({
declarations: [YourComponenToTest],
providers: [
{
provide: ActivatedRoute,
useValue: {
params: Observable.from([{id: 1}]),
},
},
]
});
然后在你的测试中它将可用,你的功能应该与此一起使用(至少是ActivatedRoute部分)
如果您希望将其存储在变量中,则可以在TestBed.get(ActivatedRoute)
函数中使用it
获取它。
不要忘记从rxjs/Rx
导入Observable,而不是从rxjs/Observable
答案 1 :(得分:10)
对于任何对如何正确使用多个属性感兴趣的人,这是您定义模拟类的方式:
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
。
答案 2 :(得分:5)
我使用paramMap而不是params面临同样的问题。这让它对我有用,至少在最简单的情况下:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Observable } from 'rxjs/Observable';
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' })
}
}
]
});
答案 3 :(得分:5)
修改上面的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',
}));
}
答案 4 :(得分:1)
我使用 RxJS Subject 而不是 Observable 作为 paramMap。这样我就可以在测试期间触发 paramMap 发出新值。
ActivatedRoute 直接在测试床配置的提供者部分中模拟:
providers: [{ provide: ActivatedRoute, useValue: { paramMap: new Subject() } }]
然后,我从测试台获得激活的路由:
activatedRoute = TestBed.inject(ActivatedRoute);
并在测试过程中需要时触发 paramMap 发出新值:
activatedRoute.paramMap.next({ get: (key: string) => 'value1'});
答案 5 :(得分:0)
就我而言,我必须创建一个新类来处理这种类型的测试,该类将允许您处理快照,queryParams和params。
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 };
}
}
这是测试的外观
import { MockActivatedRoute } from './mock-active-router';
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let activatedRouteStub: MockActivatedRoute;
beforeEach(async(() => {
activatedRouteStub = new MockActivatedRoute();
TestBed.configureTestingModule({
declarations: [MyComponent],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub }
]
}).compileComponents();
}));
it('should change params', () => {
expect(component.myparam).toBeUndefined();
expect(component.paramTwo).toBeUndefined();
activatedRouteStub.testParams = {
myparam: 'value',
paramTwo: 1
};
fixture.detectChanges();
expect(component.myparam).toEqual('value');
expect(component.paramTwo).toEqual(1);
});
https://gist.github.com/dvaJi/cf552bbe6725535955f7a5eeb92d7d2e
答案 6 :(得分:0)
在最新的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);
});
});
另请参阅this answer。
答案 7 :(得分:0)
从一开始我就一直遇到这个问题,我发现这种方法可以按我的意愿工作。例如,无需为我做间谍。
给出:
ngOnInit() {
this.some = this.activatedRoute.snapshot.paramMap.get('some') === 'some';
this.else = this.activatedRoute.snapshot.paramMap.get('else');
}
然后:
describe('SomeComponent', () => {
let component: SomeComponent;
let fixture: ComponentFixture<SomeComponent>;
let activatedRouteSpy;
beforeEach(async(() => {
activatedRouteSpy = {
snapshot: {
paramMap: convertToParamMap({
some: 'some',
else: 'else',
})
}
};
TestBed.configureTestingModule({
declarations: [ SomeComponent ],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteSpy },
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should load correct data (original stuff)', fakeAsync(() => {
component.ngOnInit();
tick(2000);
// ... do your checks ...
}));
it('should load correct data (different stuff)', fakeAsync(() => {
activatedRouteSpy.snapshot.paramMap = convertToParamMap({
some: 'some',
else: null,
});
fixture.detectChanges();
component.ngOnInit();
tick(2000);
// ... do your checks ...
}));
});