如何使用Angular Jasmine测试void方法 解决v8升v9测试报错
问题根因
- 测试代码语法错误:你在测试用例中写了
component.dataSource[0].state = 'DISABLE',但你的数据源是存在dataSource.data属性下,直接访问dataSource[0]必然返回undefined,所以触发Cannot set properties of undefined (setting 'state')报错 - Angular 9 初始化逻辑变更:Angular 9 收紧了组件属性初始化的时序校验,如果你使用的是Angular Material的MatTableDataSource,未提前实例化就直接给
dataSource.data赋值,就会偶现dataSource本身为undefined的报错,这是迁移后偶现问题的核心原因 - 依赖未完成Mock:你没有Mock
_cutsServices和swal依赖,异步订阅逻辑未受控也会引发时序类报错 - 业务代码隐含问题:原
cutState方法中的三元表达式未赋值回state属性,等于切换逻辑无效;同时存在拼写错误suscribe应为subscribe、tittle应为title
修复方案
步骤1:修复业务代码&组件初始化
先修正业务代码的拼写和逻辑,同时确保组件内的dataSource提前实例化:
// 组件内声明时直接初始化DataSource dataSource = new MatTableDataSource<any>(); public cutState(i:number) { this.loader = true; // 把切换后的值赋值回state this.dataSource.data[i].state = this.dataSource.data[i].state === 'ENABLE' ? 'DISABLE' : 'ENABLE'; // 修正拼写错误subscribe this._cutsServices.setCuts(this.dataSource.data[i]).subscribe(response => { this.loader = false; this.loadCut() // 修正拼写错误title swal.fire({ title: `!${this.nameRule} update!`, icon: 'success', showCancelButton:false, confirmButtonText:'exit' }); }, error => { this.loader = false swal.fire({ title:'Error', icon: error, showCancelButton:false, confirmButtonText: 'Exit' }); }); }
步骤2:编写完整Mock测试用例
使用Jasmine自带的Spy功能Mock所有外部依赖,用fakeAsync控制异步时序:
import { fakeAsync, flush } from '@angular/core/testing'; import { MatTableDataSource } from '@angular/material/table'; import { of, throwError } from 'rxjs'; // Mock cutsServices const mockCutsService = jasmine.createSpyObj('CutsService', ['setCuts']); // Mock swal spyOn(window, 'swal' as any).and.returnValue(Promise.resolve()); const cuts = [{ name: 'epale_test', state: 'DISABLE' }]; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [CutComponent], // 注入Mock服务替代真实服务 providers: [ { provide: CutsService, useValue: mockCutsService } ] }).compileComponents(); })); beforeEach(()=> { fixture = TestBed.createComponent(CutComponent); component = fixture.componentInstance; // 提前给dataSource赋值,避免undefined component.dataSource = new MatTableDataSource(cuts); fixture.detectChanges(); }) it('should toggle state and call service correctly when cutState is triggered', fakeAsync(() => { // Mock setCuts返回成功的Observable mockCutsService.setCuts.and.returnValue(of({})); // 修正访问路径,从dataSource.data下取数据 component.dataSource.data[0].state = 'DISABLE'; component.cutState(0); // 验证state切换正确 expect(component.dataSource.data[0].state).toBe('ENABLE'); // 验证服务被调用 expect(mockCutsService.setCuts).toHaveBeenCalledWith(component.dataSource.data[0]); // 执行异步回调 flush(); // 验证loader状态重置 expect(component.loader).toBeFalse(); // 验证弹窗被调用 expect(swal.fire).toHaveBeenCalled(); })); it('should handle error correctly when setCuts fails', fakeAsync(() => { // Mock setCuts返回错误 mockCutsService.setCuts.and.returnValue(throwError(() => new Error('test error'))); component.cutState(0); flush(); expect(component.loader).toBeFalse(); expect(swal.fire).toHaveBeenCalledWith(jasmine.objectContaining({ title: 'Error' })); }));
内容的提问来源于stack exchange,提问作者Jonathan
相关产品推荐
相关产品推荐

