在 Angular 路由定义中发布测试数据
Issues testing data in an Angular route definition
我正在尝试测试一个基于路由中的数据动态设置标题的组件,但我在测试位上遇到了问题。
我正在尝试模拟路由数据,但在我获取数据的方法中 findRouteData
它在调试测试时最终未定义。
我可以直观地验证组件本身是否按照我的要求执行,但是我在模拟路由数据位时遇到了问题。
就像我说的,一切都在被调用,但是我的 routerState 模拟没有正常工作。数据未定义。
如何正确模拟路由数据?
@Injectable()
class FakeRouter {
url: string;
subject = new Subject();
events = this.subject.asObservable();
routerState = {
snapshot: {
root: {
data: {title: 'Title'}
}
}
};
navigate(url: Array<string>): void {
this.url = url.join('');
this.triggerNavEvents(this.url);
}
triggerNavEvents(url: string): void {
this.subject.next(new NavigationEnd(0, url, null));
}
}
组件本身:
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router';
import { Subject } from 'rxjs/Subject';
@Component({
selector: 'hq-page-title',
templateUrl: './page-title.component.html',
styleUrls: ['./page-title.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PageTitleComponent implements OnInit, OnDestroy {
public title;
private unsubscribe = new Subject<boolean>();
constructor(private router: Router, private cdRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.router.events
.filter((event: any) => event instanceof NavigationEnd)
.takeUntil(this.unsubscribe)
.subscribe(() => {
const test = this.router;
const test1 = this.router.routerState;
const routeData = this.findRouteData(this.router.routerState.snapshot.root);
if (routeData.hasOwnProperty('title')) {
this.title = routeData.title;
this.cdRef.detectChanges();
}
});
}
ngOnDestroy(): void {
this.unsubscribe.next();
this.unsubscribe.complete();
}
/**
* Returns the route data
*
* Check out the following links to see why it was implemented this way:
* @link https://github.com/angular/angular/issues/19420
* @link https://github.com/angular/angular/issues/11812#issuecomment-346637722
*
* @param {ActivatedRouteSnapshot} root
* @returns {any}
*/
private findRouteData(root: ActivatedRouteSnapshot) {
let data = <any>{};
while (root) {
if (root.children && root.children.length) {
root = root.children[0];
} else if (root.data) {
data = {...data, ...root.data};
return data;
} else {
return data;
}
}
}
}
示例路线:
{
path: 'settings',
data: {title: 'Settings'},
}
测试:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PageTitleComponent } from './page-title.component';
import {NavigationEnd, Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import {Subject} from 'rxjs/Subject';
import {Injectable} from '@angular/core';
@Injectable()
class FakeRouter {
url: string;
subject = new Subject();
events = this.subject.asObservable();
routerState = {
snapshot: {
root: {
data: {title: 'Title'}
}
}
};
navigate(url: Array<string>): void {
this.url = url.join('');
this.triggerNavEvents(this.url);
}
triggerNavEvents(url: string): void {
this.subject.next(new NavigationEnd(0, url, null));
}
}
describe('PageTitleComponent', () => {
let component: PageTitleComponent;
let fixture: ComponentFixture<PageTitleComponent>;
let router: Router;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [
PageTitleComponent
],
providers: [
{ provide: router, useClass: FakeRouter }
]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(PageTitleComponent);
component = fixture.componentInstance;
router = TestBed.get(Router);
fixture.detectChanges();
});
}));
it('should create', () => {
expect(component).toBeTruthy();
});
describe('the title should be title', () => {
beforeEach(async(() => {
router.navigate(['']);
}));
it('the title should be title', () => {
expect(component.title).toBe('Title');
});
});
});
我解决了我自己的问题,因为这些事情经常发生。
我没有尝试进入 router.routerState
获取路线数据,而是改用 ActivatedRoute
,并使用了其中的快照。我之前回避了该实现,因为我发现订阅 ActivatedRoute
的 data
属性 不会产生路由 data
。它最终只是一个空白对象。
但是,我发现如果我查看 ActivatedRoute
一旦路线事件达到 NavigationEnd
,我要查找的所有数据都在那里。
ActivatedRoute
更容易模拟。在 TestBed.configureTestingModule()
内,我将此片段添加到提供者:
{
provide: ActivatedRoute,
useValue: {
snapshot: {
data: {title: 'Title'}
}
},
},
我的主要 TS 文件中的更改如下:
constructor(private router: Router, private activatedRoute: ActivatedRoute, private cdRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.router.events
.filter((event: any) => event instanceof NavigationEnd)
.takeUntil(this.unsubscribe)
.subscribe(() => {
const routeData = this.findRouteData(this.activatedRoute.snapshot);
if (routeData.hasOwnProperty('title')) {
this.title = routeData.title;
this.cdRef.detectChanges();
}
});
}
我从模拟路由器中删除了这个位:
routerState = {
snapshot: {
root: {
data: {title: 'Title'}
}
}
};
真的没有太多变化。
注意:我可能仍然遗漏了一些东西。如果您认为我是,请告诉我。
我正在尝试测试一个基于路由中的数据动态设置标题的组件,但我在测试位上遇到了问题。
我正在尝试模拟路由数据,但在我获取数据的方法中 findRouteData
它在调试测试时最终未定义。
我可以直观地验证组件本身是否按照我的要求执行,但是我在模拟路由数据位时遇到了问题。
就像我说的,一切都在被调用,但是我的 routerState 模拟没有正常工作。数据未定义。
如何正确模拟路由数据?
@Injectable()
class FakeRouter {
url: string;
subject = new Subject();
events = this.subject.asObservable();
routerState = {
snapshot: {
root: {
data: {title: 'Title'}
}
}
};
navigate(url: Array<string>): void {
this.url = url.join('');
this.triggerNavEvents(this.url);
}
triggerNavEvents(url: string): void {
this.subject.next(new NavigationEnd(0, url, null));
}
}
组件本身:
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router';
import { Subject } from 'rxjs/Subject';
@Component({
selector: 'hq-page-title',
templateUrl: './page-title.component.html',
styleUrls: ['./page-title.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PageTitleComponent implements OnInit, OnDestroy {
public title;
private unsubscribe = new Subject<boolean>();
constructor(private router: Router, private cdRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.router.events
.filter((event: any) => event instanceof NavigationEnd)
.takeUntil(this.unsubscribe)
.subscribe(() => {
const test = this.router;
const test1 = this.router.routerState;
const routeData = this.findRouteData(this.router.routerState.snapshot.root);
if (routeData.hasOwnProperty('title')) {
this.title = routeData.title;
this.cdRef.detectChanges();
}
});
}
ngOnDestroy(): void {
this.unsubscribe.next();
this.unsubscribe.complete();
}
/**
* Returns the route data
*
* Check out the following links to see why it was implemented this way:
* @link https://github.com/angular/angular/issues/19420
* @link https://github.com/angular/angular/issues/11812#issuecomment-346637722
*
* @param {ActivatedRouteSnapshot} root
* @returns {any}
*/
private findRouteData(root: ActivatedRouteSnapshot) {
let data = <any>{};
while (root) {
if (root.children && root.children.length) {
root = root.children[0];
} else if (root.data) {
data = {...data, ...root.data};
return data;
} else {
return data;
}
}
}
}
示例路线:
{
path: 'settings',
data: {title: 'Settings'},
}
测试:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PageTitleComponent } from './page-title.component';
import {NavigationEnd, Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import {Subject} from 'rxjs/Subject';
import {Injectable} from '@angular/core';
@Injectable()
class FakeRouter {
url: string;
subject = new Subject();
events = this.subject.asObservable();
routerState = {
snapshot: {
root: {
data: {title: 'Title'}
}
}
};
navigate(url: Array<string>): void {
this.url = url.join('');
this.triggerNavEvents(this.url);
}
triggerNavEvents(url: string): void {
this.subject.next(new NavigationEnd(0, url, null));
}
}
describe('PageTitleComponent', () => {
let component: PageTitleComponent;
let fixture: ComponentFixture<PageTitleComponent>;
let router: Router;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [
PageTitleComponent
],
providers: [
{ provide: router, useClass: FakeRouter }
]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(PageTitleComponent);
component = fixture.componentInstance;
router = TestBed.get(Router);
fixture.detectChanges();
});
}));
it('should create', () => {
expect(component).toBeTruthy();
});
describe('the title should be title', () => {
beforeEach(async(() => {
router.navigate(['']);
}));
it('the title should be title', () => {
expect(component.title).toBe('Title');
});
});
});
我解决了我自己的问题,因为这些事情经常发生。
我没有尝试进入 router.routerState
获取路线数据,而是改用 ActivatedRoute
,并使用了其中的快照。我之前回避了该实现,因为我发现订阅 ActivatedRoute
的 data
属性 不会产生路由 data
。它最终只是一个空白对象。
但是,我发现如果我查看 ActivatedRoute
一旦路线事件达到 NavigationEnd
,我要查找的所有数据都在那里。
ActivatedRoute
更容易模拟。在 TestBed.configureTestingModule()
内,我将此片段添加到提供者:
{
provide: ActivatedRoute,
useValue: {
snapshot: {
data: {title: 'Title'}
}
},
},
我的主要 TS 文件中的更改如下:
constructor(private router: Router, private activatedRoute: ActivatedRoute, private cdRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.router.events
.filter((event: any) => event instanceof NavigationEnd)
.takeUntil(this.unsubscribe)
.subscribe(() => {
const routeData = this.findRouteData(this.activatedRoute.snapshot);
if (routeData.hasOwnProperty('title')) {
this.title = routeData.title;
this.cdRef.detectChanges();
}
});
}
我从模拟路由器中删除了这个位:
routerState = {
snapshot: {
root: {
data: {title: 'Title'}
}
}
};
真的没有太多变化。
注意:我可能仍然遗漏了一些东西。如果您认为我是,请告诉我。