Angular Jasmine单元测试spec中import与provider配置及报错排查
Angular单元测试TestBed配置规则
不用死记配置项,按下面的规则区分就行:
- 放到
imports数组的内容:带@NgModule装饰器的模块类。就是你写业务代码时,在@NgModule({imports: []})里引入的那些模块,比如ReactiveFormsModule、RouterTestingModule、第三方库的功能模块都放这。 - 放到
providers数组的内容:通过依赖注入获取的服务/令牌。就是你写在类构造函数里、要Angular注入器帮你创建实例的对象,这些内容你可以传真实服务类,也可以传自定义的mock测试对象,单元测试优先用mock隔离外部依赖。 - 不需要进TestBed配置的内容:文件顶部import的纯工具函数、TypeScript类型、常量,这些是代码直接调用的,不走Angular依赖注入系统,完全不需要配置。你之前spec文件里引入的
getApp、signOut、addDoc、collection、getFirestore都属于这类,属于多余导入。
报错原因
你的AuthService构造函数声明了三个需要注入的依赖:
Auth(来自@angular/fire/auth):测试模块没有提供该依赖的实现,这也是当前报错的直接原因Router:你引入了RouterTestingModule,该模块已经内置了Router的测试实现,这部分配置正确AlertController(来自@ionic/angular):测试模块同样没有提供该依赖的实现,解决完Auth的报错后会继续报这个依赖缺失的错误
另外你的服务代码里有个不利于测试的写法:类属性直接初始化userCollection = collection(getFirestore(getApp()), 'users'),这行代码在服务实例化时会直接执行,调用Firebase全局方法,不走依赖注入,哪怕你provider配置全了,这行还是会尝试连接真实Firebase导致测试失败,建议把Firestore相关依赖也改成构造函数注入的形式,方便测试时mock。
修复方案
单元测试要保证隔离性,不要连接真实Firebase、不要触发真实弹窗、不要执行真实路由跳转,所有外部依赖全部用mock桩对象替代,不要引入完整的第三方生产模块。修正后的spec代码如下:
import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AuthService } from './auth.service'; import { Auth } from '@angular/fire/auth'; import { AlertController } from '@ionic/angular'; describe('AuthService', () => { let service: AuthService; // 创建所有外部依赖的mock对象 const mockAuth = jasmine.createSpyObj('Auth', []); const mockAlertInstance = jasmine.createSpyObj('Alert', ['present']); const mockAlertController = jasmine.createSpyObj('AlertController', ['create']); mockAlertController.create.and.resolveTo(mockAlertInstance); beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule], providers: [ AuthService, { provide: Auth, useValue: mockAuth }, { provide: AlertController, useValue: mockAlertController } ] }); service = TestBed.inject(AuthService); }); it('should be created', () => { expect(service).toBeTruthy(); }); });
其他组件的spec文件报同样错误的原因完全一致:只要组件/服务的构造函数注入了AuthService或者它依赖的Auth、AlertController等对象,但是测试模块没有提供对应依赖的实现,就会抛出NullInjectorError,按照上面的规则补全mock依赖即可。
内容的提问来源于stack exchange,提问作者D.Hodges
相关产品推荐
相关产品推荐

