在我应用的根组件中,我正在为md-icon
定义自定义SVG图标。当单元测试显示自定义图标的组件时,我收到错误。似乎错误可能是由于我的子组件测试中没有使用/初始化我的根组件。
在设置测试模块时,有没有办法模拟或添加这些自定义图标(或md-icon
)?我只想在我正在测试的组件中定义图标,但我知道其他组件也需要它们。
错误:
Uncaught Error: Error in ./CustomerComponent class CustomerComponent - inline template:34:19 caused by: __WEBPACK_IMPORTED_MODULE_4_rxjs_Observable__.Observable.throw is not a function
从模板中删除自定义图标可以解决错误。
我的模板使用如下自定义图标:
<md-icon svgIcon="vip">vip</md-icon>
根组件初始化图标如下:
this.iconRegistry.addSvgIcon(
'vip',
this.sanitizer.bypassSecurityTrustResourceUrl('assets/icons/vip.svg') as string,
);
我设置了这样的测试组件:
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
SharedModule,
CoreModule,
FormsModule,
ReactiveFormsModule,
],
providers: [
{
provide: Router,
useClass: class {
navigate = jasmine.createSpy('navigate');
},
},
{
provide: ActivatedRoute,
useValue: {
data: {
subscribe: (fn: (value: Data) => void) => fn({
customer: CUSTOMER,
company: COMPANY,
}),
},
},
},
{
provide: UtilityService,
useClass: UtilityServiceMock,
},
// etc...
],
declarations: [
CustomerComponent,
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA,
],
})
.compileComponents();
}));
答案 0 :(得分:1)
回答我自己的问题:
经过多次试用/错误,例如嘲笑MdIconRegistry
或使用componentOverride()
等没有运气的项目,我不再在我的测试中使用共享模块。
相反,我使用模拟版本的类直接在我的测试模块中声明了MdIcon组件。
describe(`CustomerComponent`, () => {
let component: CustomerComponent;
let fixture: ComponentFixture<CustomerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
ReactiveFormsModule,
MdButtonModule,
],
providers: [
OVERLAY_PROVIDERS,
{
provide: Router,
useClass: class {
navigate = jasmine.createSpy('navigate');
},
},
{
provide: ActivatedRoute,
useValue: {
data: {
subscribe: (fn: (value: Data) => void) => fn({
customer: customer,
company: COMPANY,
}),
},
params: Observable.of({
customerId: customerId,
}),
},
},
],
declarations: [
CustomerComponent,
// Declare my own version of MdIcon here so that it is available for the CustomerComponent
MdIconMock,
],
});
fixture = TestBed.createComponent(CustomerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it(`should exist`, () => {
expect(component).toBeTruthy();
});
});
MdIconMock只是一个与选择器匹配的空白类:
import { Component } from '@angular/core';
@Component({
template: '',
// tslint:disable-next-line
selector: 'md-icon, mat-icon',
})
// tslint:disable-next-line
export class MdIconMock {
}
注意:由于TSLint规则指定了类名/选择器的前缀/格式,我需要为此模拟禁用TSLint。
答案 1 :(得分:1)
这是一个迟到的答案。万一有人遇到这个问题,除了OP的解决方案之外还有其他选择(这也很好):
使用forRoot()
导入MaterialModulelet iconRegistry = TestBed.get(MdIconRegistry);
let sanitizer = TestBed.get(DomSanitizer);
获取注入的MdIconRegistry和DomSanitizer
iconRegistry.addSvgIcon( 'some-icon',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/some-icon.svg'));
像在普通应用中一样配置
public class ShippingCharge {
public decimal basePrice { get; set; }
public int baseCount { get; set; }
public decimal unitPrice { get; set; }
}
答案 2 :(得分:1)
我能够使用overrideModule
方法来存根MdIcon。文档很稀疏,但我能够找到一个GitHub issue Angular团队成员讨论如何覆盖声明。我们的想法是从MdIconModule
中删除组件,以便我们可以声明我们自己的模拟图标组件。
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TestedComponent ],
imports: [
RouterTestingModule.withRoutes([]),
SharedModule,
],
})
.overrideModule(MdIconModule, {
remove: {
declarations: [MdIcon],
exports: [MdIcon]
},
add: {
declarations: [MockMdIconComponent],
exports: [MockMdIconComponent]
}
})
.compileComponents();
}));
MockMdIconComponent
定义非常简单
@Component({
selector: 'md-icon',
template: '<span></span>'
})
class MockMdIconComponent {
@Input() svgIcon: any;
@Input() fontSet: any;
@Input() fontIcon: any;
}
我使用这种方法,因为我没有单独导入Material模块,我不希望我的测试必须进行Http调用才能获得svg图标。 MockMdIconComponent
可以在测试模块中声明,但我选择在模块覆盖中声明/导出它,以便我可以将对象提取到测试助手中。
答案 3 :(得分:0)
基于@Chic的答案,因为我到处都有图标,所以我做了一个测试平台补丁:
import {TestBed} from '@angular/core/testing';
import {MatIconModule, MatIcon} from '@angular/material/icon';
import {Component, Input} from '@angular/core';
export function PatchTestBedMatIcons() {
const original = TestBed.configureTestingModule;
TestBed.configureTestingModule = (moduleDef) => {
return original(moduleDef)
.overrideModule(MatIconModule, {
remove: {
declarations: [MatIcon],
exports: [MatIcon]
},
add: {
declarations: [MockMatIconComponent],
exports: [MockMatIconComponent]
}
});
};
}
@Component({
selector: 'mat-icon',
template: '<span></span>'
})
class MockMatIconComponent {
@Input() svgIcon: any = null;
@Input() fontSet: any = null;
@Input() fontIcon: any = null;
}
然后在组件中进行简单的测试:
import {PatchTestBedMatIcons} from 'src/app/patchTestBedIcons';
PatchTestBedMatIcons();