Karma中导入OverlayModule无效,遭遇依赖注入相关错误
Angular Karma测试:ToastNotificationService注入错误解决
问题重现
- 导入
ToastNotificationService时触发错误:
R3InjectorError(DynamicTestModule)[ToastNotificationService -> Overlay -> Overlay]:
NullInjectorError: No provider for Overlay!
- 导入
OverlayModule后出现新错误:
inject() must be called from an injection context
问题根源
- 第一个错误是因为
ToastNotificationService依赖Angular CDK的Overlay服务,但测试模块未正确导入提供Overlay的依赖模块。 - 第二个错误是因为手动通过
useClass提供ToastNotificationService时,未在Angular的注入上下文环境中初始化服务。第三方组件库的服务通常需要通过导入其对应的模块来确保完整的依赖链。
解决方案
- 导入组件库的Notification模块:替换手动提供
ToastNotificationService的方式,直接导入NotificationModule——该模块会自动配置所有所需的服务提供者(包括ToastNotificationService和其依赖的Overlay)。 - 使用测试专用HTTP模块:将
HttpClientModule替换为HttpClientTestingModule,避免在测试环境中初始化真实HTTP客户端。 - 清理冗余提供者配置:移除
providers数组中手动配置的ToastNotificationService,避免与模块提供的服务冲突。
修改后的测试代码
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { AlertsComponent } from './alerts.component'; import { RouterTestingModule } from '@angular/router/testing'; import { HttpProvider, StorageProvider, ToastMessageProvider } from '@ln/radix-providers-ng12'; import { AlertServiceProvider } from 'src/shared/providers/alert-service-provider.service'; import { NotificationModule } from 'ln-components-angular/notification'; import { AlertResultProvider } from 'src/shared/providers/AlertResult.provider'; import { SelectedItemService } from '../../../../../_shared-business/src/services/selected-item.service'; import { AlertSupportLinksService } from '../../../../../_shared-business/src/services/AlertSupportLinks.service'; import { PaginationServiceProvider } from '@ln/pagination-module-ng12'; import { BackGroundService } from 'src/shared/services/background.service'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { IconsRegistry } from 'ln-components-angular/icon'; import { ModalService } from 'ln-components-angular/modal'; describe('FrAlertsComponent', () => { let component: AlertsComponent; let fixture: ComponentFixture<AlertsComponent>; beforeEach(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule, HttpClientTestingModule, // 替换为测试专用HTTP模块 NotificationModule // 导入组件库模块,自动提供所需服务 ], declarations: [AlertsComponent], providers: [ StorageProvider, AlertServiceProvider, { provide: HttpProvider, useClass: HttpProvider }, AlertResultProvider, SelectedItemService, AlertSupportLinksService, IconsRegistry, PaginationServiceProvider, ModalService, BackGroundService, ] }) .compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(AlertsComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should change active tab on click', () => { const tabElements = fixture.debugElement.queryAll(By.css('li')); // 假设初始状态下第一个标签页处于激活状态 expect(tabElements[0].classes['active']).toBeTrue(); // 点击第二个标签页 tabElements[1].triggerEventHandler('click', null); fixture.detectChanges(); // 第一个标签页应不再激活,第二个标签页变为激活状态 expect(tabElements[0].classes['active']).toBeFalse(); expect(tabElements[1].classes['active']).toBeTrue(); }); });
额外说明
- 第三方组件库的服务通常封装在对应模块中,直接导入模块是最可靠的方式,可避免手动配置依赖时的上下文问题。
HttpClientTestingModule提供模拟HTTP客户端,适合单元测试场景,不会发送真实网络请求。
内容的提问来源于stack exchange,提问作者guiz
相关产品推荐
相关产品推荐

