You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular单元测试:正确Mock AngularFireStore集合get调用的问题

问题:Mock AngularFireStore复杂调用失败,测试报错

编辑说明

今日取得了一些进展,但同一流程中遇到新错误,以下是更新后的问题。

我需要Mock AngularFireStore的复杂调用,测试调用服务的importData函数时一直报错。最终的get调用应返回供校验的数组,但Mock配置不正确,导致测试失败。

目前Mock版本2已接近可用,解决了之前的函数相关错误,但现在第一个IF语句检查返回数组长度时失败,返回值为undefined,触发错误。

有意思的是:

  • 若把Mock里的最终返回改成return Promise.resolve()(不传数组),错误变为:Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'docs')
  • 传入数组时,错误变为读取undefined的length属性

我怀疑和fakeasync的处理有关,试过几种写法,当前测试用的是fakeasync。另外,如果改成普通的async加/不加await,测试显示“通过”但控制台仍抛出错误:

TypeError: Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'length')
TypeError: Cannot read properties of undefined (reading 'length')


service.ts

importData(Id: number) { 
  this.afs.collection<data>('users/' + this.authService.liveUid)
    .ref.where('id', '==', Id)
    .get()
    .then( a => { 
      if (a.docs.length == 0) { 
        if (validID(Id, 8, 9)) { 
          this.http.get<DataImport>('https://url' + Id).subscribe( 
            //do stuff 
          ) 
        } else { 
          this.urlError = true 
          this.spinnerImported = true 
        } 
      } else { 
        this.spinnerImported = true 
        this.sameImportName = a.docs[0].get('name') 
      } 
    }) 
}

注:原代码存在语法错误,已修正'users/' + this.authService.liveUid + .ref中的多余加号

service.spec.ts

describe('ImportService', () => { 
  let service: ImportService; 
  beforeEach(() => { 
    TestBed.configureTestingModule( { 
      providers: [ 
        { provide: HttpClient, useValue: httpClientMock }, 
        { provide: AngularFirestore, useValue: AngularFirestoreMock }, 
        { provide: AuthService, useValue: AuthServiceMock }, 
      ] 
    }); 
    service = TestBed.inject(ImportService); 
  }); 
  it('should be created', fakeasync (() => { 
    service.importData(32198712) 
    tick () 
  })); 
});

AngularFirestoreMock(版本1)

export const AngularFirestoreMock = jasmine.createSpyObj('AngularFirestoreMock', { 
  'doc': { set: function () { return Promise.resolve() } }, 
  'collection':{ get: function () { return Promise.resolve() } } 
})

AngularFirestoreMock(版本2)

export const AngularFirestoreMock = jasmine.createSpyObj('AngularFirestoreMock', { 
  'doc': { set: function () { return Promise.resolve() } }, 
  'collection': { 
    'ref': { 
      where: () => { 
        return { 
          get: () => { 
            return Promise.resolve([{ name: 'test'}]) 
          } 
        } 
      } 
    } 
  }
})

解决方法

问题核心是Mock返回的结构和AngularFirestore实际返回的QuerySnapshot不匹配:实际get()返回的是包含docs数组的对象,而非直接返回数组。

修正后的AngularFirestoreMock

让get()返回模拟的QuerySnapshot对象,包含docs属性,每个doc还要实现get()方法:

export const AngularFirestoreMock = jasmine.createSpyObj('AngularFirestoreMock', {
  'doc': { set: () => Promise.resolve() },
  'collection': () => ({
    ref: {
      where: () => ({
        get: () => Promise.resolve({
          // 模拟QuerySnapshot的docs数组
          docs: [
            {
              get: (field: string) => {
                if (field === 'name') return 'test';
                return null;
              }
            }
          ]
        })
      })
    }
  })
});

若要模拟空结果(触发a.docs.length === 0分支),只需把docs设为空数组:

get: () => Promise.resolve({ docs: [] })

测试优化建议

  1. 使用fakeAsync时,用flush()替代tick(),确保所有异步操作完成,避免控制台残留报错:
it('should handle existing data', fakeAsync(() => {
  service.importData(32198712);
  flush(); // 触发所有异步操作完成
  expect(service.sameImportName).toBe('test');
  expect(service.spinnerImported).toBe(true);
}));
  1. 若用async/await,建议修改importData让它返回Promise,方便测试等待:
// 修改service.ts的importData,返回Promise
importData(Id: number): Promise<void> { 
  return this.afs.collection<data>('users/' + this.authService.liveUid)
    .ref.where('id', '==', Id)
    .get()
    .then( a => { 
      // ...原有逻辑
    }) 
}

// 测试代码
it('should handle existing data', async () => {
  await service.importData(32198712);
  expect(service.sameImportName).toBe('test');
  expect(service.spinnerImported).toBe(true);
});

内容的提问来源于stack exchange,提问作者Rusty Shackleford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 13:14:54