如何在单元测试中通过MatIconRegistry添加svg文件?

时间:2019-04-02 10:14:41

标签: angular testing svg jasmine

我目前正在我的角度应用程序中实施“单元测试”。但是,如果我运行它们,则会收到类似于以下警告/错误的多个警告/错误:'Error retrieving icon: Unable to find icon with the name ":myIcon"'。我怀疑这可能是由于未将svg添加到我的MatIconRegistry中引起的。我通常在我的AppComponent中这样做,就像这样:

constuctor(private iconRegistry: MatIconRegistry,
           private sanitizer: DomSanitizer,
           ...) {
        iconRegistry.addSvgIcon('myIcon', sanitizer.bypassSecurityTrustResourceUrl('./assets/icons/myIcon.svg'));
}

但是,如果我运行另一个组件的单元测试,它将不会执行,因此不会将我的svg添加到注册表中。我已经尝试将其添加到相应组件的.spec文件中,如下所示:

fdescribe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let iconRegistry;
  let sanitizer;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        ...
      ],
      imports: [
        ...
      ],
      providers: [
        ...
      ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    iconRegistry = TestBed.get(MatIconRegistry);
    sanitizer = TestBed.get(DomSanitizer);
    iconRegistry.addSvgIcon('myIcon', sanitizer.bypassSecurityTrustResourceUrl('./../../assets/icons/myIcon.svg'));
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create component', () => {
    expect(component).toBeTruthy();
  });
});

如果我运行此命令,它将不起作用。它只是返回不同的错误消息:

Error retrieving icon: <svg> tag not found

我最初的想法是我在路径上犯了一个错误,但是尝试了其他各种路径后,我确定情况并非如此。

有人知道如何解决这个问题吗?或者也许有更好的方法,因为我必须在要测试的每个组件中添加svg图标,这有点多余。

6 个答案:

答案 0 :(得分:13)

可以做到:

...
import { MatIcon } from '@angular/material/icon';
import { MatIconTestingModule } from '@angular/material/icon/testing';

describe('MyComponent', () => {
  ...

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [MyComponent, MatIcon],
      imports: [MatIconTestingModule],
    }).compileComponents();
  }));

...

});

这将在没有http请求的情况下生成测试图标渲染

答案 1 :(得分:3)

模拟mat-icon选择器,并在单元测试的顶部添加以下组件

@Component({
    selector: 'mat-icon',
    template: '<span></span>'
})
class MockMatIconComponent {
    @Input() svgIcon: any;
    @Input() fontSet: any;
    @Input() fontIcon: any;

    selector: 'mat-icon',
    template: '<span></span>'
})

然后按如下所示在单元测试中覆盖MatIconModule

beforeEach(() => {
    TestBed.configureTestingModule({
        declarations: [ ...],
        providers: [ ...  ],
        imports: [ MatIconModule, NoopAnimationsModule ]
    })
    .overrideModule(MatIconModule, {
    remove: {
       declarations: [MatIcon],
       exports: [MatIcon]
    },
    add: {
        declarations: [MockMatIconComponent],
        exports: [MockMatIconComponent]
   }
   })
  .compileComponents();

运行单元测试时,您将不再遇到'Error retrieving icon: Unable to find icon with the name ":myIcon"'问题

答案 2 :(得分:3)

为此花了几个小时。看起来Angular现在提供了FakeMatIconRegistry。它减少了约90%的业障警告,但仍然有一些...

要做很多这样的事情:

TestBed.configureTestingModule({
    declarations: [ MyComponent ],
    imports: [
        ...
        MatIconModule,
    ],
    providers: [
        ...
        { provide: MatIconRegistry, useClass: FakeMatIconRegistry }
    ]
}).compileComponents();

答案 3 :(得分:0)

使用typemoq进行模拟;以下对我有用:

const mockIconRegistry = Mock.ofType<MatIconRegistry>();
mockIconRegistry.setup(s => s.getNamedSvgIcon(It.isAny(), It.isAny())).returns(() => of(Mock.ofType<SVGElement>().object));

然后

providers: [{ provide: MatIconRegistry, useFactory: () => mockIconRegistry.object }]

答案 4 :(得分:0)

您可以做的是安装ng-mocks并使用 MockModule 在测试中模拟MatIconModule,例如:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    imports: [MockModule(MatIconModule)],
  }).compileComponents();
}));

答案 5 :(得分:0)

还有另一种方法来避免此问题。 Spectator(这是Angular单元测试中非常棒的工具)具有方便的 shallow 标志,该标志基本上可以防止所有子组件(包括mat-icon)被编译,但可以作为简单的HTML标记进行处理代替。

const createComponent = createComponentFactory({
  component: MyComponent,
  imports: [
    ...
  ],
  mocks: [
    ...
  ],
  providers: [
    ...
  ],
  shallow: true // <-- ignore child components
});

启用此标志后,您不再需要导入MatIconModule ,也减少了测试运行时间,因为Angular在运行测试。这样一来,您就可以在编写单元测试时真正专注于组件本身,而不必担心您根本不需要的东西。

如果你问我,这是双赢的:)