Angular Jest单元测试如何mock Promise覆盖signMessage的if分支
问题描述
现有如下方法实现:
async signMessage(xml, mbo): Promise<any> { try { const isSignatureAppAlive = await this.pingDigitalSignatureApp().toPromise(); if (isSignatureAppAlive.alive) { try { this.signedMsg = await this.getSignedMsg(xml, mbo).toPromise(); return this.signedMsg; } catch (er) { return this.handleSignatureAppErrorCodes(er.error.errorCode); } } } catch (e) { this.showSignatureInfoModal(); return this.handleError({ message: 'empty' }); } }
getSignedMsg(msg, mbo): Observable<any> { this.signDocument.title = ''; this.signDocument.content = btoa(msg); this.signDocument.contentType = 'application/xml'; this.signDocument.uriOrIdToSign = ''; this.postParam.certificateAlias = mbo; this.postParam.digestAlgorithm = 'SHA1'; this.postParam.documents = []; this.postParam.documents.push(this.signDocument); this.postParam.signingMethod = 'xmldsig'; this.postParam.envelopingMethod = 'enveloped'; return this.http.post<any>(SERVICE_URL + 'sign', this.postParam, httpOptions).pipe(); }
目前已编写覆盖错误捕获场景的测试用例:
service.signMessage('xml', 'mbo').catch((errCode) => { expect(errCode).toBe('empty'); done(); });
需要补充什么逻辑才能覆盖方法内的if语句分支?是否需要mock Promise?曾尝试编写如下测试用例:
it('signMessage', async (done) => { const spy = spyOn(service, 'pingDigitalSignatureApp').and.returnValue( new Promise((resolve) => resolve('someVal')), ); service.pingDigitalSignatureApp(); expect(spy).toHaveBeenCalled(); done(); });
该用例执行后未达到预期效果,测试逻辑并未进入if语句分支,需要相关解决方案建议。
问题原因
之前写的测试没进入if分支,核心问题有3个:
pingDigitalSignatureApp本身返回Observable类型,直接mock返回Promise,和原代码调用.toPromise()转Promise的逻辑不匹配- mock的resolve值是字符串
'someVal',原代码判断条件是isSignatureAppAlive.alive,字符串不存在alive属性,会被判定为假值,自然进不去if块 - 测试逻辑里直接调用了
service.pingDigitalSignatureApp(),根本没有调用被测目标signMessage方法,逻辑不可能走到if分支
覆盖if分支的测试方案
要覆盖if (isSignatureAppAlive.alive === true)分支,按以下规则mock即可:
- 把
pingDigitalSignatureAppmock成返回Observable,这个Observable吐出的对象必须带alive: true属性 - 要覆盖if块内签名成功的分支,同步mock
getSignedMsg方法返回成功的Observable - 要覆盖if块内签名失败的内层catch分支,mock
getSignedMsg返回抛出指定结构错误的Observable
注意:用到的
of、throwError是RxJS提供的构造方法,需要从rxjs包导入,不要手动返回原生Promise,避免和原方法的Observable调用链不匹配。
参考测试用例
覆盖「签名服务存活+签名成功」分支
it('should enter if branch when signature app is alive and sign success', async () => { // mock ping方法返回alive为true的Observable spyOn(service, 'pingDigitalSignatureApp').and.returnValue( of({ alive: true }) ); // mock getSignedMsg返回成功的签名结果 const mockSignedResult = 'mock-signed-msg'; spyOn(service, 'getSignedMsg').and.returnValue(of(mockSignedResult)); spyOn(service, 'handleSignatureAppErrorCodes'); // 调用被测方法 const result = await service.signMessage('test-xml', 'test-mbo'); // 断言逻辑符合预期 expect(service.pingDigitalSignatureApp).toHaveBeenCalled(); expect(service.getSignedMsg).toHaveBeenCalledWith('test-xml', 'test-mbo'); expect(result).toBe(mockSignedResult); expect(service.signedMsg).toBe(mockSignedResult); expect(service.handleSignatureAppErrorCodes).not.toHaveBeenCalled(); });
覆盖「签名服务存活+签名接口报错」分支
it('should handle sign error when signature app is alive but sign request failed', async () => { // mock ping返回alive:true spyOn(service, 'pingDigitalSignatureApp').and.returnValue(of({ alive: true })); // mock getSignedMsg返回错误,错误结构匹配原代码取值逻辑er.error.errorCode const mockErrorCode = 'ERR_SIGN_FAILED'; const mockError = { error: { errorCode: mockErrorCode } }; spyOn(service, 'getSignedMsg').and.returnValue(throwError(() => mockError)); spyOn(service, 'handleSignatureAppErrorCodes').and.returnValue(mockErrorCode); const result = await service.signMessage('test-xml', 'test-mbo'); expect(service.getSignedMsg).toHaveBeenCalledWith('test-xml', 'test-mbo'); expect(service.handleSignatureAppErrorCodes).toHaveBeenCalledWith(mockErrorCode); expect(result).toBe(mockErrorCode); });
如果要覆盖「签名服务不存活」的分支(if条件不满足场景),只需要把ping方法的返回值改成of({ alive: false })即可,此时if块不会执行,方法最终会返回undefined,可根据业务逻辑补充对应断言。
内容的提问来源于stack exchange,提问作者fadingbeat
相关产品推荐
相关产品推荐

