单元测试 Angular 独立组件,未使用重写提供程序

问题描述 投票:0回答:2

我有一个角度组件,使用我想在单元测试中模拟的类。所以我在测试的提供者部分覆盖了它。 但该组件仍然调用真实的类,并且我收到错误。

这在没有独立组件的旧项目中工作得很好,但我认为我需要对独立组件做一些不同的事情?但我不明白什么:-(

我将其简化为这个测试用例:

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();
    });

});

angular ionic-framework jasmine
2个回答
2
投票

我找到了以下解决方案。但我不明白为什么在这种情况下我需要这个,而在其他情况下重写的提供程序可以工作......

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();
    });

});

0
投票

您遇到的问题是由于如何在独立组件中提供和注入服务造成的。由于服务是直接在 @Component 装饰器中的提供者数组中定义的,因此 Angular 会为该特定组件创建这些服务的新实例。这可能会使模拟复杂化,因为单元测试中的 TestBed 需要覆盖组件级提供程序。

要解决此问题,请按照下列步骤操作: 在 TestBed 中使用

overrideProvider
在测试设置中,您可以使用
TestBed.overrideProvider
将组件级别提供的服务实例替换为模拟服务。

以下是如何执行此操作的示例:

beforeEach(async () => {
    
    await TestBed.configureTestingModule({
        imports: [],
    }).overrideProvider(ModalController, { useValue: modalControllerMock })
    .compileComponents();

    ...
});
© www.soinside.com 2019 - 2024. All rights reserved.