为何对navigateByUrl间谍对象的expect断言需使用正则匹配器?
问题现象
对Angular Router的navigateByUrl方法创建间谍后,两种断言方式出现明显不一致:
可正常运行的代码
const routerSpy = spyOn(TestBed.inject(Router), 'navigateByUrl'); const thing = fixture.debugElement.query(By.css('.list .list-group-item')); expect(thing).not.toBeNull(); thing.triggerEventHandler('click', new Event('click')); expect(routerSpy).toHaveBeenCalledOnceWith(jasmine.stringMatching(/\/thing\/1/), jasmine.any(Object));
断言失败的代码
... expect(routerSpy).toHaveBeenCalledOnceWith('/thing/1', jasmine.any(Object));
后者触发的错误信息:
Expected $[0] = /thing/1 to equal '/thing/1'.
触发点击的元素为:
<a [routerLink]="['./thing', thing.id]" ...>
核心原因
问题根源在于Angular Router对routerLink的处理逻辑:
- 当绑定
[routerLink]="['./thing', thing.id]"这种数组形式的相对路径时,Angular会基于当前激活路由,将其解析为UrlTree对象——这是Router内部用来描述路由状态的结构化对象,而非原始字符串。 - 使用
jasmine.stringMatching断言时,Jasmine会自动调用UrlTree的toString()方法,将其转换为对应的路径字符串(如/thing/1),再用正则匹配,因此能成功匹配。 - 直接用字符串
'/thing/1'断言时,Jasmine会执行严格相等性检查:实际传入的UrlTree对象和字符串类型完全不同,自然匹配失败。错误信息里的/thing/1只是UrlTree对象toString()后的展示值,并非实际传入的字符串参数。
可选解决方法
- 继续使用正则匹配:这是最简便的方式,直接用
jasmine.stringMatching匹配UrlTree转换后的字符串。 - 序列化UrlTree后断言:利用Router的
serializeUrl方法将UrlTree转为字符串再匹配:const router = TestBed.inject(Router); // 先确认调用参数是UrlTree,再匹配其字符串形式 expect(routerSpy).toHaveBeenCalledOnceWith(jasmine.any(UrlTree), jasmine.any(Object)); expect(router.serializeUrl(routerSpy.calls.first().args[0])).toMatch(/\/thing\/1/); - 构建预期UrlTree断言:提前创建与
routerLink解析结果一致的UrlTree对象:const router = TestBed.inject(Router); const activatedRoute = TestBed.inject(ActivatedRoute); const expectedUrlTree = router.createUrlTree(['./thing', 1], { relativeTo: activatedRoute }); expect(routerSpy).toHaveBeenCalledOnceWith(expectedUrlTree, jasmine.any(Object));
内容的提问来源于stack exchange,提问作者Derek White
相关产品推荐
相关产品推荐

