Angular Snackbar服务Jasmine测试报错:MatSnackBar无提供者求助
解决Jasmine测试SnackbarService时的NullInjectorError问题
你遇到的错误核心原因是:测试模块没有为MatSnackBar和NgZone提供依赖项,你的SnackbarService依赖这两个服务,但测试配置中没有告诉TestBed如何创建它们。下面是两种解决方案,推荐使用第一种(模拟依赖),这是单元测试的最佳实践:
方案1:使用模拟依赖(推荐)
单元测试的核心是隔离要测试的服务——我们不需要测试Angular Material的MatSnackBar本身,只需要验证SnackbarService是否正确调用了它的方法,以及是否在NgZone中执行逻辑。
修改你的snackbar.service.spec.ts文件如下:
import { TestBed } from '@angular/core/testing'; import { SnackbarService } from './snackbar.service'; import { MatSnackBar } from '@angular/material/snack-bar'; import { NgZone } from '@angular/core'; describe('SnackbarService', () => { let service: SnackbarService; let mockMatSnackBar: jasmine.SpyObj<MatSnackBar>; let mockNgZone: jasmine.SpyObj<NgZone>; beforeEach(() => { // 创建MatSnackBar的模拟对象,仅保留我们需要的`open`方法 const snackBarSpy = jasmine.createSpyObj('MatSnackBar', ['open']); // 创建NgZone的模拟对象,让`run`方法直接执行传入的回调函数 const ngZoneSpy = jasmine.createSpyObj('NgZone', ['run']); ngZoneSpy.run.and.callFake((callback) => callback()); TestBed.configureTestingModule({ providers: [ SnackbarService, { provide: MatSnackBar, useValue: snackBarSpy }, { provide: NgZone, useValue: ngZoneSpy } ] }); // 获取服务和模拟对象 service = TestBed.inject(SnackbarService); mockMatSnackBar = TestBed.inject(MatSnackBar) as jasmine.SpyObj<MatSnackBar>; mockNgZone = TestBed.inject(NgZone) as jasmine.SpyObj<NgZone>; }); it('should be created', () => { expect(service).toBeTruthy(); }); it('should call open method and delegate correctly', () => { const testMsg = 'Hello'; const testAction = 'X'; const testDuration = 1000; // 调用服务的open方法 service.open(testMsg, testAction, testDuration); // 验证服务的open方法被正确调用 expect(service.open).toHaveBeenCalledWith(testMsg, testAction, testDuration); // 验证NgZone.run被触发(确保逻辑在Angular区域内执行) expect(mockNgZone.run).toHaveBeenCalled(); // 验证MatSnackBar的open方法被传入正确参数 expect(mockMatSnackBar.open).toHaveBeenCalledWith(testMsg, testAction, { duration: testDuration }); }); });
为什么推荐这种方式?
- 隔离依赖:测试只关注
SnackbarService自身的逻辑,不受第三方组件实现的影响 - 测试更稳定:不需要加载整个Angular Material模块,测试执行速度更快
- 验证更细致:可以精准验证依赖服务的调用细节,比如参数是否正确、是否在NgZone中执行
方案2:导入MatSnackBarModule(不推荐,仅作参考)
如果你只是想快速让测试通过,可以直接在测试模块中导入MatSnackBarModule,但这会引入不必要的依赖,不符合单元测试的纯粹性原则:
import { TestBed } from '@angular/core/testing'; import { SnackbarService } from './snackbar.service'; import { MatSnackBarModule } from '@angular/material/snack-bar'; describe('SnackbarService', () => { beforeEach(() => TestBed.configureTestingModule({ imports: [MatSnackBarModule] // 导入Material模块提供依赖 })); it('should be created', () => { const service: SnackbarService = TestBed.inject(SnackbarService); expect(service).toBeTruthy(); }); it('should call open()', () => { const service: SnackbarService = TestBed.inject(SnackbarService); const spy = spyOn(service, 'open'); service.open('Hello', 'X', 1000); expect(spy).toHaveBeenCalled(); }) });
这种方式虽然能快速解决错误,但测试会依赖Angular Material的具体实现,不够独立。
内容的提问来源于stack exchange,提问作者blk
相关产品推荐
相关产品推荐

