Jest测试报错:无法解析LoginService的全部参数
Angular Jest单元测试:LoginService依赖注入解析失败问题
报错信息
Error: Can't resolve all parameters for LoginService: (?).
at syntaxError (/Users/wilson.gonzalez/Desktop/proyecto_andes/external/npm/node_modules/tslib/tslib.es6.js:170:49) at CompileMetadataResolver._getDependenciesMetadata (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/identifiers.ts:69:99) at CompileMetadataResolver._getTypeMetadata (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/identifiers.ts:66:95) at CompileMetadataResolver._getInjectableTypeMetadata (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/identifiers.ts:69:99) at CompileMetadataResolver.getProviderMetadata (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/identifiers.ts:69:99) at /Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/identifiers.ts:69:99 at Array.forEach (<anonymous>) at CompileMetadataResolver._getProvidersMetadata (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/identifiers.ts:69:99) at CompileMetadataResolver.getNgModuleMetadata (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/identifiers.ts:62:21) at JitCompiler._loadModules (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/compile_metadata.ts:482:40) at JitCompiler._compileModuleAndAllComponents (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/compile_metadata.ts:482:40) at JitCompiler.compileModuleAndAllComponentsAsync (/Users/wilson.gonzalez/Desktop/proyecto_andes/packages/compiler/src/compile_metadata.ts:482:40) at CompilerImpl.compileModuleAndAllComponentsAsync (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js:615:29) at TestingCompilerImpl.compileModuleAndAllComponentsAsync (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js:583:29) at TestBedViewEngine.compileComponents (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/@angular/core/bundles/core-testing.umd.js:3360:29) at Function.TestBedViewEngine.compileComponents (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/@angular/core/bundles/core-testing.umd.js:3167:27) at /Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/src/app/views/auth/login/login.component.spec.ts:44:8 at ZoneDelegate.invoke (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/zone.js/dist/zone.js:457:160) at ProxyZoneSpec.onInvoke (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/zone.js/dist/proxy.js:150:35) at ZoneDelegate.invoke (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/zone.js/dist/zone.js:457:48) at Zone.run (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/zone.js/dist/zone.js:183:37) at Object.<anonymous> (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/jest-preset-angular/zone-patch/index.js:51:54) at Object.asyncJestLifecycle (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37) at /Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/jest-jasmine2/build/queueRunner.js:45:12 at new Promise (<anonymous>) at mapper (/Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/jest-jasmine2/build/queueRunner.js:28:19) at /Users/wilson.gonzalez/Desktop/proyecto_andes/Andes---ProyectoWeb/node_modules/jest-jasmine2/build/queueRunner.js:75:41 at processTicksAndRejections (internal/process/task_queues.js:95:5)
排查情况
移除LoginComponent构造函数中的LoginService、Router等依赖注入参数后,测试可正常执行。
测试代码
import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {LoginComponent} from './login.component'; import {LoginService} from '../../../services/login.service'; import {RouterTestingModule} from '@angular/router/testing'; import {UtilitiesService} from '../../../services/utilities.service'; import {Router} from '@angular/router'; import {HttpClient, HttpClientModule} from '@angular/common/http'; import {of} from 'rxjs'; import {CommunicatorService} from '../../../services/communicator.service'; import {LoadingService} from '../../../services/loading.service'; import {ToastrService} from 'ngx-toastr'; import {FormsModule} from '@angular/forms'; describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture<LoginComponent>; beforeEach((() => { TestBed.configureTestingModule({ imports: [RouterTestingModule, HttpClientModule, FormsModule], declarations: [ LoginComponent ], providers: [ HttpClient, LoadingService, UtilitiesService, ToastrService, { provide: Router, useValue: { navigate: jasmine.createSpy('navigate'), }, }, { provide: CommunicatorService, useValue: { http_post() { return of({}); }, }, }, LoginService, ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('Create', () => { expect(component).toBeTruthy(); }); });
组件代码(依赖注入完整版本)
import {Component, Input, OnInit} from '@angular/core'; import {LoginService} from '../../../services/login.service'; import {Router} from '@angular/router'; import {UtilitiesService} from '../../../services/utilities.service'; import {ToastrService} from 'ngx-toastr'; @Component({ selector: 'app-login', templateUrl: './login.component.html', }) export class LoginComponent implements OnInit { constructor(private loginService: LoginService, private router: Router, private utilitiesService:UtilitiesService, private toastr: ToastrService) {} private _email = ''; private _password = ''; private _isChecked = false; private _disable=false; ngOnInit(): void { this.checkIsSaveUser(); } checkIsSaveUser() { const emailSaved = localStorage.getItem('USER_ACTIVE'); if(emailSaved !== undefined && emailSaved !== 'null' && emailSaved !== ''){ this._email = emailSaved; this._isChecked = true; } } checkInfo(){ return !this.utilitiesService.validatorsFields(this._email) || !this.utilitiesService.validatorsFields(this._password)|| !this.utilitiesService.validatorsEmail(this._email); } saveUser() { if(!this._isChecked) { this._isChecked = true; localStorage.setItem('USER_ACTIVE', this._email); } else { this._isChecked = false; localStorage.setItem('USER_ACTIVE', ''); } } login(){ this.loginService.user = this._email; this.loginService.password = this._password; if(this._isChecked) { localStorage.setItem('USER_ACTIVE', this._email); } this.loginService.login().subscribe(result => { this.loginService.tokenSecret = result.data.token; this.loginService.rol = result.data.rol; this.router.navigate(['admin/tables']); }, error => { this.toastr.error(error.error.code +': ' + error.error.message, 'Error', { timeOut: 7000, }); }) } get password(): string { return this._password; } set password(value: string) { this._password = value; } get isChecked(): boolean { return this._isChecked; } set isChecked(value: boolean) { this._isChecked = value; } get disable(): boolean { return this._disable; } set disable(value: boolean) { this._disable = value; } get email(): string { return this._email; } set email(value: string) { this._email = value; } }
解决方案
1. 模拟LoginService(推荐)
单元测试应隔离真实服务的依赖,将测试模块中LoginService的直接注册替换为模拟实现:
{ provide: LoginService, useValue: { user: '', password: '', tokenSecret: '', rol: '', login: () => of({ data: { token: 'mock-token', rol: 'admin' } }) } }
这样完全避免了真实LoginService的依赖解析问题,同时能控制其行为。
2. 检查LoginService的依赖
如果必须使用真实LoginService,需确认其构造函数的所有参数都已在测试模块中提供。比如LoginService若依赖CommunicatorService,需确保测试中提供的模拟符合其调用要求。
3. 验证TypeScript配置
确保tsconfig.json中启用了装饰器元数据生成,否则Angular无法解析依赖参数:
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }
内容的提问来源于stack exchange,提问作者Alejandro Gonzalez
相关产品推荐
相关产品推荐

