Angular测试报No provider for ActivatedRoute及title读取错误如何解决
Angular单元测试报错原因及解决方案
错误原因
- 错误1:
NullInjectorError: No provider for ActivatedRoute!
LoginComponent构造函数中注入了ActivatedRoute、Router、AuthService、ToastMessageService多个依赖,但测试配置的TestBed中没有提供这些依赖的对应实例,Angular测试运行时找不到提供器就会抛出该注入错误。 - 错误2:
TypeError: Cannot read properties of undefined (reading 'title')
测试用例中写了断言expect(component.title).toEqual('login page'),但查看组件代码,title是一个无返回值的空方法,并非值为'login page'的字符串属性,类型不匹配导致报错。
解决方案
步骤1:完善测试模块的依赖配置
需要在TestBed中导入路由测试模块,同时mock所有业务服务依赖,避免后续再出现其他注入错误。
步骤2:修复title相关逻辑
如果确实需要title属性断言,将组件中的title() {}修改为title = 'login page';如果不需要该测试,直接删除对应测试用例即可。
步骤3:修正逻辑错误的测试用例
现有测试用例中expect(component.submitted).toBeTruthy()也会执行失败,组件中submitted初始值为false,只有调用onSubmit方法后才会变为true,可根据实际需求调整该用例逻辑。
修正后的完整login.component.spec.ts代码
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { LoginComponent } from './login.component'; import { DebugElement } from '@angular/core'; import { CommonModule } from '@angular/common'; import { BrowserModule, By } from '@angular/platform-browser'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CoreModule } from '../../core/core.module'; // 导入路由测试模块 import { RouterTestingModule } from '@angular/router/testing'; // 导入需要mock的服务 import { AuthService } from '../../core/services/auth.service'; import { ToastMessageService } from '../../shared/services/toast-message.service'; describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture<LoginComponent>; let de: DebugElement; let el: HTMLElement; // 模拟服务 const mockAuthService = jasmine.createSpyObj('AuthService', ['login', 'forgotPassword'], { currentUserValue: null }); const mockToastService = jasmine.createSpyObj('ToastMessageService', ['setToastMessage']); beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [LoginComponent], imports: [ CommonModule, BrowserModule, FormsModule, CoreModule, ReactiveFormsModule, // 添加路由测试模块,自动提供ActivatedRoute和Router RouterTestingModule ], // 提供所有依赖的mock实例 providers: [ { provide: AuthService, useValue: mockAuthService }, { provide: ToastMessageService, useValue: mockToastService } ] }) .compileComponents() .then(() => { fixture = TestBed.createComponent(LoginComponent); de = fixture.debugElement.query(By.css('form')); el = de.nativeElement; }); }); beforeEach(() => { fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; fixture.detectChanges(); }); // 如果你修改了组件中的title为字符串属性,保留该用例,否则删除 fit(`should have as text 'login page'`, () => { expect(component.title).toEqual('login page'); }); // 修正submitted的断言逻辑,示例改为调用onSubmit后判断 fit('should set submitted to true after form submit', () => { component.onSubmit(); expect(component.submitted).toBeTruthy(); }); fit('should call the onSubmit method', () => { fixture.detectChanges(); spyOn(component, 'onSubmit'); el = fixture.debugElement.query(By.css('button')).nativeElement; el.click(); expect(component.onSubmit).toHaveBeenCalledTimes(1); }); fit('form should be invalid', () => { component.loginForm.controls['email'].setValue(''); component.loginForm.controls['password'].setValue(''); expect(component.loginForm.valid).toBeFalsy(); }); fit('form should be valid', () => { component.loginForm.controls['email'].setValue('asd@asd.com'); component.loginForm.controls['password'].setValue('aAAada'); expect(component.loginForm.valid).toBeTruthy(); }); });
可选组件调整(如果保留title测试用例)
将login.component.ts中的title() {}替换为:
title = 'login page';
内容的提问来源于stack exchange,提问作者Aminson
相关产品推荐
相关产品推荐

