Angular使用Jest运行测试报Can't resolve all parameters for CustomTranslationLoader错
问题描述
我正在为登录页面编写第一个测试用例,运行测试时出现两个异常:
- 抛出错误
Can't resolve all parameters for CustomTranslationLoader: (?) - 测试断言
expect(component).toBeTruthy()失败,返回值为undefined,打印fixture也为undefined
登录组件代码(login.component.ts)
import {Component, OnInit} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; import {AuthenticationService} from '../../../services/authentication.service'; import {HttpErrorResponse} from "@angular/common/http"; import _ from "lodash"; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.scss'], }) export class LoginComponent implements OnInit { loginForm: FormGroup; private returnUrl: string; invalidCredentials = false; error = ''; forbiddenUser = false; constructor(private formBuilder: FormBuilder, private route: ActivatedRoute, private router: Router, private authenticationService: AuthenticationService) { } username: string; password: string; ngOnInit() { this.loginForm = this.formBuilder.group({ username: ['', Validators.required], password: ['', Validators.required] }); } authenticate() { let code; let accessToken; let refreshToken; code = localStorage.getItem('code'); if (code) { code = window.atob(code); accessToken = localStorage.getItem('accessToken'); refreshToken = localStorage.getItem('refreshToken'); if (accessToken && refreshToken) { accessToken = window.atob('accessToken'); refreshToken = window.atob('refreshToken'); } } } onSubmit() { this.invalidCredentials = false; this.forbiddenUser = false; this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; // stop here if form is invalid if (this.loginForm.invalid) { return; } this.authenticationService.login(this.loginForm.value.username, this.loginForm.value.password).subscribe({ next: (data) => { this.router.navigateByUrl(this.returnUrl); }, error: (errRes: HttpErrorResponse) => { if (errRes.status === 403) { if (errRes.error.ErrorMessage === "loginFailure") { this.invalidCredentials = true; } else if (_.has(errRes, 'error.username')) { this.forbiddenUser = true; } } } }); } }
测试文件代码(login.component.spec.ts)
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { LoginComponent } from './login.component'; import {AuthenticationService} from '../../../services/authentication.service'; import { createComponent, Page, CommonTestUtilsModule, provideMockObject } from '@webex/common/test-utils'; import { CommonUiMomentumChModule } from '@webex/common/ui/momentum-ch'; const MockAuthenticationService = { login: jest.fn(), }; class LoginComponentPage extends Page<LoginComponent> { constructor(fixture: ComponentFixture<LoginComponent>) { super(fixture); } } describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture<LoginComponent> let page: LoginComponentPage; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ LoginComponent ], providers: [ provideMockObject(AuthenticationService), { provide: AuthenticationService, useValue: MockAuthenticationService, }, ], imports: [ CommonUiMomentumChModule, CommonTestUtilsModule, FormsModule, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }).compileComponents(); })); beforeEach(() => { ({ fixture, component, page } = createComponent(LoginComponent, LoginComponentPage)); fixture.detectChanges(); }); it('should create component', () => { expect(component).toBeTruthy() }); });
问题原因分析
- 翻译加载器依赖缺失:引入的
CommonUiMomentumChModule内置了CustomTranslationLoader,该加载器需要注入对应依赖,但测试模块未提供对应依赖,导致整个测试模块初始化失败,后续createComponent无法正常返回实例。 - 响应式表单依赖缺失:登录组件使用了响应式表单API,测试模块只导入了模板驱动表单的
FormsModule,未导入ReactiveFormsModule,会导致FormBuilder注入失败。 - 核心路由依赖未Mock:登录组件构造函数依赖
ActivatedRoute、Router,测试模块中未提供这两个依赖的Mock实现,注入失败。 - 依赖重复声明:
AuthenticationService在providers中重复声明了两次,会导致依赖注入冲突。
修复方案
首先在测试文件顶部补充必要导入:
import { ReactiveFormsModule } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; import { TranslateLoader } from '@ngx-translate/core'; import { of } from 'rxjs';
修改测试模块的配置如下:
TestBed.configureTestingModule({ declarations: [ LoginComponent ], providers: [ // 覆盖翻译加载器,解决CustomTranslationLoader依赖问题 { provide: TranslateLoader, useValue: { get: jest.fn(() => of({})) } }, // Mock路由依赖 { provide: Router, useValue: { navigateByUrl: jest.fn() } }, { provide: ActivatedRoute, useValue: { snapshot: { queryParams: {} } } }, // 保留自定义的AuthenticationService Mock,删除重复的provideMockObject声明 { provide: AuthenticationService, useValue: MockAuthenticationService, }, ], imports: [ CommonUiMomentumChModule, CommonTestUtilsModule, FormsModule, ReactiveFormsModule // 新增响应式表单模块 ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }).compileComponents();
修改完成后重新运行测试即可:
- 翻译加载器的依赖错误消失,TestBed可以正常初始化
- 所有组件依赖都有对应Mock,
createComponent可以正常返回fixture和component实例 - 基础断言
expect(component).toBeTruthy()可以正常通过
内容的提问来源于stack exchange,提问作者pranami
相关产品推荐
相关产品推荐

