Angular中使用Jest、Jasmine测试模态确认对话框服务的问题求助
Testing Angular ConfirmationDialogService with Jasmine/Karma
作为测试新手,你遇到的问题其实很常见——我们需要测试服务的行为,而不是仅仅模拟方法返回值。直接spyOn服务的confirm方法虽然能快速模拟返回,但没法验证服务内部是否正确和NgbModal交互。下面是一套完整的测试方案,一步步来:
第一步:配置测试环境
首先,我们需要在测试文件中导入必要的模块,然后用Jasmine创建NgbModal的mock对象,避免真的打开模态框。
import { TestBed } from '@angular/core/testing'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { ConfirmationDialogService } from './confirmation-dialog.service'; import { ConfirmationDialogComponent } from './confirmation-dialog.component'; describe('ConfirmationDialogService', () => { let service: ConfirmationDialogService; let modalServiceSpy: jasmine.SpyObj<NgbModal>; let mockModalRef: Partial<NgbModalRef>; beforeEach(() => { // 1. 创建模拟的模态框引用,我们可以控制它的返回结果 mockModalRef = { componentInstance: {}, // 用于模拟组件实例的属性 result: Promise.resolve(true) // 默认模拟用户点击确认的情况 }; // 2. 创建NgbModal的spy对象,只模拟我们需要的`open`方法 modalServiceSpy = jasmine.createSpyObj('NgbModal', ['open']); modalServiceSpy.open.and.returnValue(mockModalRef as NgbModalRef); // 3. 配置TestBed,注入服务和mock的NgbModal TestBed.configureTestingModule({ providers: [ ConfirmationDialogService, { provide: NgbModal, useValue: modalServiceSpy } ] }); // 4. 获取服务实例和spy对象 service = TestBed.inject(ConfirmationDialogService); });
第二步:基础测试
先验证服务能正常创建:
it('should be created', () => { expect(service).toBeTruthy(); });
第三步:测试服务的核心逻辑
我们需要验证:
- 调用
confirm时,是否正确打开了ConfirmationDialogComponent - 是否正确传递了标题、消息、按钮文本等参数
- 是否返回了模态框的结果
it('should open dialog with correct component, size and component properties', async () => { const testTitle = 'Delete Item?'; const testMessage = 'Are you sure you want to delete this item?'; const testOkText = 'Delete'; const testCancelText = 'Keep'; const testSize = 'lg'; // 调用服务的confirm方法 await service.confirm(testTitle, testMessage, testOkText, testCancelText, testSize); // 验证modalService的open方法是否被正确调用 expect(modalServiceSpy.open).toHaveBeenCalledWith( ConfirmationDialogComponent, { size: testSize } ); // 验证组件实例的属性是否被正确赋值 const dialogComponent = mockModalRef.componentInstance; expect(dialogComponent.title).toBe(testTitle); expect(dialogComponent.message).toBe(testMessage); expect(dialogComponent.btnOkText).toBe(testOkText); expect(dialogComponent.btnCancelText).toBe(testCancelText); });
第四步:测试不同用户操作的返回值
接下来测试用户点击确认和取消时,服务的返回结果是否符合预期:
情况1:用户点击确认
it('should resolve to true when user confirms', async () => { const result = await service.confirm('Test Title', 'Test Message'); expect(result).toBe(true); });
情况2:用户点击取消
这里需要注意:你的ConfirmationDialogComponent在用户点击取消时,是调用modalRef.close(false)还是modalRef.dismiss()?
- 如果是
close(false),模态框的result会resolve为false:
it('should resolve to false when user cancels', async () => { // 修改mock的result为false mockModalRef.result = Promise.resolve(false); const result = await service.confirm('Test Title', 'Test Message'); expect(result).toBe(false); });
- 如果是
dismiss(),模态框的result会reject,这时候我们需要测试reject的情况:
it('should reject when user cancels via dismiss', async () => { mockModalRef.result = Promise.reject('User canceled'); // 使用expectAsync来测试Promise的reject await expectAsync(service.confirm('Test Title', 'Test Message')) .toBeRejectedWith('User canceled'); });
为什么不直接spyOn服务的confirm方法?
直接spyOn(ConfirmationDialogService, 'confirm')虽然能快速模拟返回值,但这种测试是“黑盒”的——你没有验证服务内部是否正确和NgbModal交互,比如有没有正确传递参数给模态框组件。而我们上面的测试是“白盒”的,能确保服务的逻辑完全符合预期。
内容的提问来源于stack exchange,提问作者Conn
相关产品推荐
相关产品推荐

