使用Jasmine测试Angular 5时DomSanitizer方法调用报错问题
我正在用Jasmine对Angular 5应用做单元测试,现在要测试一个依赖DomSanitizer的函数:
loadImage() { this.image = this.sanitizer.bypassSecurityTrustStyle(`url(${this.assetUrl})`); }
这个函数运行正常,DomSanitizer已经注入到构造函数里,语法也没问题。
我的单元测试代码是这样的:
it('loads the Image', () => { component.loadImage() fixture.detectChanges(); expect(component.imageUrl).toBe('...'); });
另外,我已经把DomSanitizer加到TestBed的providers里了:
beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule, CommonModule ], declarations: [ MyComponent ], providers: [ DomSanitizer ], schemas: [ NO_ERRORS_SCHEMA ] }) .compileComponents(); }));
但运行测试时Jasmine抛出错误:
ERROR: 'Unhandled Promise rejection:', '_this.sanitizer.bypassSecurityTrustStyle is not a function', '; Zone:', 'angular', '; Task:', 'Promise.then', '; Value:', TypeError: _this.sanitizer.bypassSecurityTrustStyle is not a function
求解决思路,谢谢!
这个问题我之前在做Angular测试的时候也碰到过,核心原因是你直接把DomSanitizer加到TestBed的providers里是不对的——因为DomSanitizer本身是个抽象类,测试环境不会自动给它提供可用的具体实例,所以调用它的方法时才会报错。给你几个可行的解决思路:
方案1:手动Mock DomSanitizer(最推荐,测试隔离性好)
不需要依赖Angular的内部实现,自己模拟一个sanitizer对象,只实现我们需要的方法就行。这样测试的时候完全可控,还能验证方法的调用情况:
修改TestBed的providers配置:
beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule, CommonModule ], declarations: [ MyComponent ], providers: [ { provide: DomSanitizer, useValue: { // 测试里不需要真的做 sanitize,直接返回输入即可 bypassSecurityTrustStyle: (value: string) => value } } ], schemas: [ NO_ERRORS_SCHEMA ] }) .compileComponents(); }));
如果想验证bypassSecurityTrustStyle是否被正确调用,还可以在测试里加个spy:
it('loads the Image and calls sanitizer correctly', () => { // 获取注入的sanitizer实例 const sanitizer = TestBed.inject(DomSanitizer); // 监听bypassSecurityTrustStyle方法 spyOn(sanitizer, 'bypassSecurityTrustStyle').and.returnValue('sanitized-result'); // 设置测试用的assetUrl component.assetUrl = 'test-image.png'; // 调用目标函数 component.loadImage(); // 验证方法调用参数是否正确 expect(sanitizer.bypassSecurityTrustStyle).toHaveBeenCalledWith('url(test-image.png)'); // 验证组件的image属性是否被正确赋值 expect(component.image).toBe('sanitized-result'); });
方案2:使用Angular的具体实现类
如果你想使用Angular真实的sanitizer逻辑,可以导入DomSanitizerImpl(这是DomSanitizer的具体实现),然后在providers里配置:
首先导入:
import { DomSanitizerImpl } from '@angular/platform-browser/src/security/dom_sanitizer';
然后修改providers:
providers: [ { provide: DomSanitizer, useClass: DomSanitizerImpl } ]
不过要注意,DomSanitizerImpl是Angular的内部API,可能在后续版本有变动,如果你用的是Angular 5,这个是没问题的。
方案3:调整测试模块的导入
有时候导入BrowserModule可以让测试环境自动提供正确的DomSanitizer实例,不过这种方式会引入很多不必要的模块,可能让测试变慢,所以不是最优解:
imports: [ RouterTestingModule, CommonModule, BrowserModule ],
总结一下,最推荐的是方案1,既保证了测试的隔离性,又能灵活验证函数的行为,避免依赖Angular内部实现带来的风险。
内容的提问来源于stack exchange,提问作者Alex Verzea

