Angular 18:懒加载模块单元测试替代SpyNgModuleFactoryLoader方案咨询
测试带
canMatch守卫的懒加载模块(Angular 13+) 路由配置示例(截图内容):包含
canMatch守卫与loadChildren的懒加载路由定义,例如:{ path: 'auth', canMatch: [AuthGuard], loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule) }
针对SpyNgModuleFactoryLoader已废弃的情况,以下是Angular 13+版本测试带loadChildren的懒加载模块的替代方案:
1. 用RouterTestingModule.withRoutes模拟路由逻辑
无需真实加载懒加载模块,直接通过RouterTestingModule提供模拟路由,配合mock守卫实现,验证导航行为:
import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Router, Location } from '@angular/common'; import { provideMockStore } from '@ngrx/store/testing'; import { UserActions } from './store/user.actions'; import { MockData } from './mock.data'; import { AuthGuard } from './auth.guard'; // 定义空的mock组件用于测试 @Component({ template: '' }) class MockDashboardComponent {} describe('Auth Route Navigation', () => { let router: Router; let location: Location; let store: MockStore; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([ { path: 'auth', canMatch: [AuthGuard], children: [ { path: 'dashboard', component: MockDashboardComponent }, { path: '', redirectTo: 'dashboard', pathMatch: 'full' } ] } ]) ], declarations: [MockDashboardComponent], providers: [ provideMockStore(), // 若需mock守卫逻辑,可替换为自定义实现 // { provide: AuthGuard, useValue: { canMatch: () => Promise.resolve(true) } } ] }).compileComponents(); router = TestBed.inject(Router); location = TestBed.inject(Location); store = TestBed.inject(MockStore); await router.initialNavigation(); }); it('导航到"auth"时重定向到/auth/dashboard', async () => { store.dispatch(UserActions.loginSuccess({ data: MockData.UserLoggedIn })); await router.navigate(['auth']); expect(location.path()).toBe('/auth/dashboard'); }); });
2. 使用RouterTestingHarness(Angular 14+)
Angular 14+推出的RouterTestingHarness简化了路由测试流程,适合复杂场景:
import { TestBed } from '@angular/core/testing'; import { RouterTestingHarness } from '@angular/router/testing'; import { provideRoutes } from '@angular/router'; import { provideMockStore } from '@ngrx/store/testing'; import { UserActions } from './store/user.actions'; import { MockData } from './mock.data'; import { AuthGuard } from './auth.guard'; @Component({ template: '' }) class MockDashboardComponent {} describe('Auth Route Navigation', () => { let harness: RouterTestingHarness; let store: MockStore; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [MockDashboardComponent], providers: [ provideMockStore(), provideRoutes([ { path: 'auth', canMatch: [AuthGuard], children: [ { path: 'dashboard', component: MockDashboardComponent }, { path: '', redirectTo: 'dashboard', pathMatch: 'full' } ] } ]) ] }).compileComponents(); store = TestBed.inject(MockStore); harness = await RouterTestingHarness.create(); }); it('导航到"auth"时重定向到/auth/dashboard', async () => { store.dispatch(UserActions.loginSuccess({ data: MockData.UserLoggedIn })); await harness.navigateByUrl('/auth'); expect(harness.location.path()).toBe('/auth/dashboard'); }); });
3. MockloadChildren加载函数
如果需要验证loadChildren的触发逻辑,可以直接mock其返回值,避免真实加载模块:
// 在路由配置中mock loadChildren RouterTestingModule.withRoutes([ { path: 'auth', canMatch: [AuthGuard], // Mock懒加载模块加载,返回Promise包装的mock模块 loadChildren: () => Promise.resolve({ ngModule: class MockAuthModule {}, providers: [] }) } ])
核心注意事项
- 无需依赖废弃的
SpyNgModuleFactoryLoader,通过模拟路由或mock守卫即可完成测试 canMatch守卫的逻辑应单独编写单元测试,路由测试只需验证守卫结果对导航的影响- 使用
async/await替代then回调,提升测试代码的可读性与稳定性
内容的提问来源于stack exchange,提问作者chetan
相关产品推荐
相关产品推荐

