我找到的所有例子都只展示了如何测试一个调用navigateByUrl的组件。我有一个服务可以做到这一点。
@Injectable({
providedIn: 'root'
})
export class BreadcrumbService {
constructor(private router: Router, private activateRoute: ActivatedRoute) {}
/**
* Return a array of current path
*
*/
getCurrentRouterPath(): Array<string> {
return this.router.url.split('/').filter(path => path);
}
}
我试图通过模拟Router来测试它。
import {TestBed, async, inject} from '@angular/core/testing';
import {BreadcrumbService} from './breadcrumb.service';
import {ActivatedRoute, Router} from '@angular/router';
class MockRouter {
navigateByUrl(url: string) {
return { url };
}
}
describe('BreadcrumbService', () => {
let service: BreadcrumbService;
let activateRoute: ActivatedRoute;
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
{ provide: Router, useClass: MockRouter },
{ provide: ActivatedRoute }
]
});
activateRoute = TestBed.inject(ActivatedRoute);
service = TestBed.inject(BreadcrumbService);
}));
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should return an array of route path', inject([Router], (router: MockRouter) => {
const spy = spyOn(router, 'navigateByUrl');
router.navigateByUrl('/giveaway/all');
const arrayPath = service.getCurrentRouterPath();
expect(arrayPath).not.toBeNull();
expect(arrayPath).not.toBeUndefined();
expect(arrayPath).toEqual(['giveaway', 'all']);
}));
});
...但url总是未定义,因此... cannot read property 'split of undefined occurs.
我的尝试
所以我决定模拟url属性本身来避免这种情况。
class MockRouter {
private _url = '';
get url(): string {
return this._url;
}
set url(value: string) {
this._url = value;
}
}
现在测试运行了,但url属性总是空的,所以我得到一个空数组,所以测试失败了。
好吧,所以我可以通过直接在mock上显式地设置url来使测试通过,但这在我看来是错误的,因为它完全跳过了导航步骤(你注意到我删除了 navigateByUrl
从模拟班)。)
it('should return an array of route path', inject([Router], (router: MockRouter) => {
router.url = '/giveaway/all';
const arrayPath = service.getCurrentRouterPath();
expect(arrayPath).not.toBeNull();
expect(arrayPath).not.toBeUndefined();
expect(arrayPath).toEqual(['giveaway', 'all']);
}));
所以这里有3个问题
我是否不应该模拟路由器,而应该注入真正的路由器,并在测试中实际触发一个导航事件?(试过了,从来没有失败过!--见下面的说明)
如果我只用上面的工作方法,因为它实际上是在服务中测试方法,那么(1)是否重要?
我是否需要测试这个方法,因为它实际上所依赖的都是其他开发者已经测试过的东西(即Angular路由器,数组分割和过滤器)。
注意
我继承了这段代码,原来是像下面这样,测试从来没有失败过。
import {TestBed} from '@angular/core/testing';
import {BreadcrumbService} from './breadcrumb.service';
import {RouterTestingModule} from '@angular/router/testing';
import {ActivatedRoute, Router} from '@angular/router';
import {NgZone} from '@angular/core';
describe('BreadcrumbService', () => {
let service: BreadcrumbService;
let router: Router;
let activateRoute: ActivatedRoute;
let ngZone: NgZone;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([])]
});
router = TestBed.inject(Router);
activateRoute = TestBed.inject(ActivatedRoute);
service = TestBed.inject(BreadcrumbService);
ngZone = TestBed.inject(NgZone);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should receive a array of route path', () => {
ngZone.run(() => {
router.navigateByUrl('/giveaway/all').then(() => {
const arrayPath = service.getCurrentRouterPath();
expect(arrayPath).not.toBeNull();
expect(arrayPath).not.toBeUndefined();
expect(arrayPath.length).toEqual(2);
});
});
});
});
哪个 屡试不爽 的导航长度或平等测试。所以连这都通过了。
expect(arrayPath.length).toEqual(200)
还有这个
expect(arrayPath.length).toEqual(0)
这看起来非常奇怪。
如果有人能够解释为什么使用实际的路由器和导航没有工作,我将非常感激!我可以找到的所有例子都只展示了如何测试调用navigateByUrl的组件。
当我需要测试一些使用路由器的东西时,我尽量模拟。测试时,路由总是很棘手。当我看你的服务时,它做的事情非常简单,而且可以不考虑导航的情况下进行测试。所以我就按以下方式进行测试
const testUrl = '/some/test/url';
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
{
provide: Router,
useValue: {
url: testUrl
}
}
]
});
activateRoute = TestBed.inject(ActivatedRoute);
service = TestBed.inject(BreadcrumbService);
}));
it('should return an array of route path', () => {
const arrayPath = service.getCurrentRouterPath();
expect(arrayPath).not.toBeNull();
expect(arrayPath).not.toBeUndefined();
expect(arrayPath).toEqual(['some', 'test', 'url']);
}));
你的测试不应该关心如何 url
是设置。那是别人的工作。你应该假设 Router
经得起考验和 url
是在导航时正确设置的。否则,我们的测试就会变得太复杂,难以编写和维护。
回答你的另一个问题,为什么测试总是通过了?
在内部运行异步代码 it
可能会导致意外的行为。你的测试总是通过,因为测试在async函数执行之前完成。所以,你的测试总是通过,因为测试在async函数执行之前就完成了。expect....
当测试已经完成(并通过)时,就会执行。要保持测试直到运行代码,可以使用 done
如下
it('should receive a array of route path', (done) => {
ngZone.run(() => {
router.navigateByUrl('/giveaway/all').then(() => {
const arrayPath = service.getCurrentRouterPath();
expect(arrayPath).not.toBeNull();
expect(arrayPath).not.toBeUndefined();
expect(arrayPath.length).toEqual(2);
done(); // mark the test as done
});
});
});
关于测试异步函数的更多信息,你可以阅读它。此处