angular:如何在单元测试中模拟扩展类

时间:2018-06-01 09:22:19

标签: javascript angular unit-testing jasmine

我有一个注入服务(EntityApi),它扩展了一个类(BaseApi)。在我的规范中,我喜欢用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检查控制台。我期待你是   在轨道上!! 记录但收到不应该在这里......

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

2 个答案:

答案 0 :(得分:0)

您要做的事情不起作用。依赖注入和类继承没有直接关系。这意味着您无法像这样切换服务的基类。

我认为你有两种方法可以做到这一点。

选项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扩展以调用其上的方法。

答案 1 :(得分:0)

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

spyObject = {
  methodToSpyOn(){}
};

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

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

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