我有一个角度组件,使用我想在单元测试中模拟的类。所以我在测试的提供者部分覆盖了它。 但该组件仍然调用真实的类,并且我收到错误。
这在没有独立组件的旧项目中工作得很好,但我认为我需要对独立组件做一些不同的事情?但我不明白什么:-(
我将其简化为这个测试用例:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IonicModule, ModalController } from '@ionic/angular';
import { BrowserModule } from '@angular/platform-browser';
import { Component } from '@angular/core';
// Dummy Standalone Component, just a single method that uses the ionic ModalController
@Component({
selector: 'app-test-dialog',
template: '',
imports: [
IonicModule
],
standalone: true
})
export class TestDialogComponent {
constructor(private modalCtrl: ModalController) {
}
async close() {
await this.modalCtrl.dismiss();
}
}
// Test, should override the dismiss method of the ModalController
describe('TestDialogComponent', () => {
let component: TestDialogComponent;
let fixture: ComponentFixture<TestDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
BrowserModule,
IonicModule.forRoot(),
TestDialogComponent
],
providers: [
{
// provide our own ModalController
provide: ModalController, useValue: {
dismiss: () => Promise.resolve()
}
}
]
}).compileComponents();
fixture = TestBed.createComponent(TestDialogComponent);
component = fixture.componentInstance;
});
// this should call the provided mock class
// but I get 'overlay does not exist thrown'
// because it calls the real method :-(
it('close closes the dialog', async () => {
const modalController = TestBed.inject(ModalController);
const dismissSpy = spyOn(modalController, 'dismiss');
await component.close();
expect(dismissSpy).toHaveBeenCalled();
});
});
我找到了以下解决方案。但我不明白为什么在这种情况下我需要这个,而在其他情况下重写的提供程序可以工作......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IonicModule, ModalController } from '@ionic/angular';
import { BrowserModule } from '@angular/platform-browser';
import { Component } from '@angular/core';
@Component({
selector: 'app-test-dialog',
template: '',
imports: [
IonicModule
],
standalone: true
})
export class TestDialogComponent {
constructor(private modalCtrl: ModalController) {
}
async close() {
await this.modalCtrl.dismiss();
}
}
// use a mock-class so we can directly spy on it
class ModalControllerMock {
dismiss() {
return Promise.resolve();
}
}
describe('TestDialogComponent', () => {
let component: TestDialogComponent;
let fixture: ComponentFixture<TestDialogComponent>;
const modalControllerMock = new ModalControllerMock();
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
BrowserModule,
IonicModule.forRoot(),
TestDialogComponent
]
}).compileComponents();
// use this provider instead of the provider in configureTestingModule
TestBed.overrideComponent(TestDialogComponent, {
set: {
providers: [{ provide: ModalController, useValue: modalControllerMock}],
},
});
fixture = TestBed.createComponent(TestDialogComponent);
component = fixture.componentInstance;
});
it('close closes the dialog', async () => {
const dismissSpy = spyOn(modalControllerMock, 'dismiss');
await component.close();
expect(dismissSpy).toHaveBeenCalled();
});
});
您遇到的问题是由于如何在独立组件中提供和注入服务造成的。由于服务是直接在 @Component 装饰器中的提供者数组中定义的,因此 Angular 会为该特定组件创建这些服务的新实例。这可能会使模拟复杂化,因为单元测试中的 TestBed 需要覆盖组件级提供程序。
要解决此问题,请按照下列步骤操作: 在 TestBed 中使用
overrideProvider
在测试设置中,您可以使用 TestBed.overrideProvider
将组件级别提供的服务实例替换为模拟服务。
以下是如何执行此操作的示例:
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [],
}).overrideProvider(ModalController, { useValue: modalControllerMock })
.compileComponents();
...
});