Angular 9 单元测试:如何模拟测试组件的导入? [英] Angular 9 Unit Test: how to mock import of tested component?

查看:30
本文介绍了Angular 9 单元测试:如何模拟测试组件的导入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我目前正在为 Angular 9 组件编写 Jasmine/Karma 单元测试.

So I'm currently writing a Jasmine/Karma Unit Test for an Angular 9 Component.

我的应用程序如何工作的简短摘要:我用 D3 编写了一个小 Funnel,它在类似漏斗的图表中显示给定的数据.然后我写了一个 FunnelComponent 包含这个 Funnel 并且还在实际图表旁边显示一些元信息.

A short summary how my application works: I've written a little Funnel with D3 that displays given data in a funnel-like diagram. Then I've written a FunnelComponent that contains this Funnel and also displays some meta information next to the actual diagram.

这是我要测试的组件:

funnel.component.ts

import { Component } from '@angular/core';
import { Funnel } from './d3charts/funnel.ts';
import { FunnelData } from './funnel.data';

@Component({
  selector: 'v7-funnel-component',
  templateUrl: './funnel.html'
})
export class FunnelComponent {

  private funnel: Funnel = null;

  constructor() {}

  public createFunnel(funnelData: FunnelData): void {
      this.funnel = new Funnel();
      this.funnel.setData(funnelData);
      this.funnel.draw();
  }
}

这是我对该组件的 karma-jasmine 单元测试:

And here is my karma-jasmine unit-test for that Component:

funnel.component.spec.ts

import { TestBed, ComponentFixture } from '@angular/core/testing';
import { } from 'jasmine';
import { FunnelComponent } from './funnel.component';
import { Component } from '@angular/core';
import { FunnelData } from './funnel.data';
import { Funnel } from './d3charts/funnel.ts';

@Component({selector: 'funnel', template: ''})
class FunnelStub {
  private data: FunnelData = null;

  public setData(data: FunnelData): void
  {this.data = data;}
  {}
  public draw(): void
  {}
  public update(funnelData: FunnelData): void
  {}
}

/**
 * Testing class for FunnelComponent.
 */
describe('Component: Funnel', () => {

  let component: FunnelComponent;
  let fixture: ComponentFixture<FunnelComponent>;

  beforeEach(async(() => {
    TestBed
    .configureTestingModule({
      declarations: [
        FunnelComponent,
        FunnelStub
      ],
      providers: [
        { provide: Funnel, useValue: FunnelStub}
      ]
    })
    .compileComponents()
    .then(() => {
      fixture = TestBed.createComponent(FunnelComponent);
      component = fixture.componentInstance;
    });
  }));

it('#createFunnel should set data of funnel. Filled data should set filled funnel.', () => {
    expect(component["funnel"]).toBeNull();

    let exampleFunnelData = new FunnelData("testcaption", "testdescription", 8);
    component.createFunnel(exampleFunnelData);

    expect(component["funnel"]).toBeDefined();
    expect(component["funnel"]["data"]).toBeDefined();
    expect(component.data.caption).toEqual("testcaption");
    expect(component.data.description).toEqual("testsubtext");
    expect(component.data.value).toEqual(8);
  });
});

我想在这里测试 createFunnel 方法.但我不希望我的 createFunnel 方法将真正的 Funnel 分配给 this.funnel,而是使用我的 FunnelStub.知道如何做到这一点吗?

I want to test the createFunnel method here. But I don't want that my createFunnel method assigns a real Funnel to this.funnel, but uses my FunnelStub instead. Any idea how to do this?

{ provide: Funnel, useValue: FunnelStub} 添加到我的 providers 数组没有帮助:(

Adding { provide: Funnel, useValue: FunnelStub} to my providers array didn't help :(

最好的问候,塞巴斯蒂安

Best regards, Sebastian

推荐答案

这不起作用,因为 Funnel 不是提供者:

This does not work, because Funnel is not a provider:

providers: [
  { provide: Funnel, useValue: FunnelStub}
]

最能帮助您进行测试的是为漏斗创建创建服务.

What would help you the most for testing, would be to create a service for the Funnel creation.

export abstract class FunnelProvider {
  abstract createFunnel(): Funnel;
}

此服务可以有 2 种实现方式,一种是您在应用中使用的实现,另一种是您在测试中使用的实现.

This service can then have 2 implementations, one implementation you use in your app and one that you use in your tests.

@Injectable()
export class DefaultFunnelProvider {
  createFunnel() {
    return new Funnel(); // Return real funnel object.
  }
}

@Injectable()
export class MockFunnelProvider {
  createFunnel() {
     // Return stub funnel. Assuming that they share the same interface.
    return new FunnelStub() as Funnel;
  }
}

现在您必须在您的 FunnelModule(声明您的 FunnelComponent 的模块)中定义 DefaultFunnelProvider:

Now you have to define the DefaultFunnelProvider in your FunnelModule (the module that declares your FunnelComponent):

@NgModule({
  ... // other stuff
  providers: [{provide: FunnelProvider, useClass: DefaultFunnelProvider}]
})
export class FunnelModule {}

然后在 FunnelComponent 中注入 FunnelProvider 并使用它来创建漏斗:

Then you inject the FunnelProvider in your FunnelComponent and use it to create the funnel:

export class FunnelComponent {

  private funnel: Funnel = null;

  constructor(private funnelProvider: FunnelProvider) {}

  public createFunnel(funnelData: FunnelData): void {
      this.funnel = this.funnelProvider.createFunnel();
      this.funnel.setData(funnelData);
      this.funnel.draw();
  }
}

做了很多工作,但现在很酷的是,您可以在 TestBed 中注入 FunnelProvider 的 Mock 实现:

A lot of work, but now the cool thing is that you can inject the Mock implementation of the FunnelProvider in your TestBed:

TestBed
    .configureTestingModule({
      declarations: [
        FunnelComponent,
        FunnelStub
      ],
      providers: [
        { provide: FunnelProvider, useValue: MockFunnelProvider }
      ]
    })

现在当 funnelProvider.createFunnel 被调用时,它不会返回一个真正的漏斗,而是你的漏斗存根.

Now when funnelProvider.createFunnel is called, it will not return a real funnel, but your funnel stub.

如果你必须在测试期间,你可以将组件漏斗字段转换为 FunnelStub(例如,如果它有一些特殊的方法):

If you must during your test, you can cast the components funnel field to FunnelStub (e.g. if it has some special method):

const funnelStub = component.funnel as FunnelStub;

这篇关于Angular 9 单元测试:如何模拟测试组件的导入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆