【问题标题】:angular: How to mock extends class in unit test角度:如何在单元测试中模拟扩展类
【发布时间】:2018-11-11 09:37:25
【问题描述】:

我有一个扩展类 (BaseApi) 的可注入服务 (EntityApi)。在我的规范中,我喜欢用 BaseApiStub 模拟 BaseApi。但它是徒劳的。总是调用 EntityApi。

// class
export class BaseApi { // want to mock BaseApi
    constructor(injector: Injector) {
       console.log("Should not be here...");
    }
}

// service
@Injectable()
export class EntityApi extends BaseApi {
    constructor(injector: Injector) {
       super(injector, "entity");
    }
}

// component
@Component({
  selector: 'rt-entity-list',
  templateUrl: './entity-list.component.html',
})
export class EntityListComponent {
  api: any;
  constructor(public entityApi: EntityApi) { 
    this.api = entityApi;
  }
}

// mock api
export class BaseApiStub { //mocked api
    constructor() {
      console.log("You are on track!!")
    }
    get() { }
}

// spec
describe('EntityListComponent', () => {
  let component: EntityListComponent;
  let fixture: ComponentFixture<EntityListComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [EntityListComponent],
      providers: [ { provide: BaseApi, useClass: BaseApiStub }, // mocked class.
      ],
      schemas: [NO_ERRORS_SCHEMA]
    }).compileComponents();
    
  beforeEach(() => {
    fixture = TestBed.createComponent(EntityListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

预期的行为是,在规范中编译组件时。它应该调用 BaseApiStub,而不是调用 BaseApi。我已经看到了如下解决方案。但没有运气。

export class BaseApiStub extends BaseApi { }

测试代码:stackblitz 检查控制台。我希望你是 正常!! 记录但收到不应该在这里...

无法更进一步。有人可以纠正我的错误吗?

【问题讨论】:

  • 嗨 Premkumar,您的方法是正确的,但是您是否在 BaseApiStub 中为 BaseApi 方法提供了任何虚假/模拟实现? (因为根据你的 sn-p 它是空的)
  • 我在 BaseApiStub 中添加了一个方法。仍然面临同样的问题。它正在调用 BaseApi。类模拟语法是否正确?通常,对于我曾经这样嘲笑的服务。但不确定课程。
  • 这当然取决于您的测试条件,例如如果 BaseApi 和 BaseApiStub 在您的注射器中都可用,它只会解析找到的第一个。尝试直接覆盖您的提供程序以确保,如下所示:TestBed.overrideProvider(BaseApi, BaseApiStub)
  • 我已经尝试了你的建议。通话仍然出错。我已经用 stackblitz 更新了我的问题。单元测试通过。但它不应该调用 BaseApi。检查控制台。
  • 现在很清楚了,你先注册一个真正的 Api,然后你提供另一个实例,只做这个提供者:[{ provide: EntityApi, useClass: BaseApiStub }]

标签: javascript angular unit-testing jasmine


【解决方案1】:

您尝试执行的操作不起作用。依赖注入和类继承没有直接关系。这意味着您不能像这样切换服务的基类。

在我看来,您有两种方法可以做到这一点。

选项 1:

您需要模拟 EntityApi 并在测试中提供此模拟,而不是模拟您的 BaseApi 并在您的测试中提供模拟。

选项 2:

您可以保留 BaseApi 一个简单的服务并将其作为依赖项提供,而不是让您的 EntityApi 从 BaseApi 扩展。

代替

class EntityApi extends BaseApi {
    constructor(private injector: Injector) {

你会的

class EntityApi {
    constructor(private api: BaseApi) {

如果您像这样设置 EntityApi,它不会从 BaseApi 扩展,而是将其作为依赖项。然后,您可以创建 BaseApi 的模拟,并像在测试中那样提供它。

编辑

关于您的评论:

因为我应该使用 BaseApi 中的方法,所以我不能没有扩展。

这不是真的。假设 BaseApi 有一个您想要使用的方法 foo()。扩展基类时,用法可能如下所示:

class EntityApi extends BaseApi {
    constructor(private injector: Injector) {}
    exampleMethod() {
        this.foo();
    }
}

如果你只是有依赖,你仍然可以像这样调用方法:

class EntityApi {
    constructor(private api: BaseApi) {}
    exampleMethod() {
        this.api.foo();
    }
}

你不需要从 BaseApi 扩展来调用它的方法。

【讨论】:

  • 我觉得这两个选项都不适合我的要求。 EntityApi 将动态生成。即,我有一个生成器来为我的所有表生成 api。例如CompanyApi、DepartmentApi 等。所有的 Api 都将与 BaseApi 一起扩展。 BaseApi 有一些可重用的方法。
  • 我应该写一个测试来检查生成的api是否可用。类似expect(component.api.__proto__.constructor.name).toBe('EntityApi');。所以选项1不会奏效。因为我应该使用 BaseApi 中的方法,所以我不能没有扩展。
  • 是否需要调用基础 api 上的方法并不重要,只要您可以控制生成器的模板即可。我在回答中添加了一个关于如何调用它们的示例。
  • 我同意你的观点。我试图避免this.api.foo()。想以this.foo()的身份访问。
【解决方案2】:

如果您需要模拟父类的方法(例如 Directive),您可以通过测试类的存根扩展来实现。

spyObject = {
  methodToSpyOn(){}
};

@Directive({selector: '[myDirective]'})
class MyStubDirective extends MyDirective {
  parentMethodToMock() {
    return spyObject.methodToSpyOn();
  }
}

spyOn(spyObject, 'methodToSpyOn').and.returnValue(true);

如果你的类在构造函数中有父方法调用,通常需要这种方法

【讨论】:

    猜你喜欢
    • 2019-06-05
    • 1970-01-01
    • 2017-09-04
    • 1970-01-01
    • 2022-01-10
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多