如何测试Fyne API ConfirmDialog内部的函数?
如何测试Fyne中ConfirmDialog的交互逻辑?
我用Fyne库开发时,通过ConfirmDialog确认用户是否需要覆盖存档文件,核心代码如下:
func (g *GunMenu) LoadButton(w *fyne.Window) (sb *widget.Button) { g.SaveButton = widget.NewButton("save", func() { fmt.Println("saving...") dialog.ShowConfirm("Saving...", "overwrite save?", func(b bool) { err := g.SaveData.SaveGame() if err != nil { dialog.NewError(err, *w) } }, *w) }) return g.SaveButton }
测试时只能触发按钮点击,无法模拟对话框的确认/取消操作,导致对话框内的代码无法被测试覆盖,测试代码如下:
func TestGetWidgets(t *testing.T) { tw := test.NewWindow(nil) gm := GunMenu{} gm.LoadButton(&tw) test.Tap(gm.SaveButton) test.Tap(gm.SaveButton) }
请问有哪些测试该对话框的方法?是否需要将回调函数单独定义再做单元测试?
可行的测试方案
1. 抽离回调逻辑,单独做单元测试
把对话框内的业务逻辑从匿名回调中抽离成独立函数,直接针对业务逻辑做单元测试,无需依赖Fyne的UI组件。
修改后的业务代码:
func (g *GunMenu) handleSaveConfirm(confirm bool, w fyne.Window) { if !confirm { return // 取消操作直接返回,无需执行保存 } err := g.SaveData.SaveGame() if err != nil { dialog.NewError(err, w) } } func (g *GunMenu) LoadButton(w *fyne.Window) (sb *widget.Button) { g.SaveButton = widget.NewButton("save", func() { fmt.Println("saving...") dialog.ShowConfirm("Saving...", "overwrite save?", func(b bool) { g.handleSaveConfirm(b, *w) }, *w) }) return g.SaveButton }
对应的单元测试:
func TestHandleSaveConfirm(t *testing.T) { // 模拟带Mock SaveData的GunMenu实例 gm := &GunMenu{ SaveData: &MockSaveData{ /* 用mock库模拟SaveGame的返回值 */ }, } mockWindow := test.NewWindow(nil) // 测试确认保存的情况 gm.handleSaveConfirm(true, mockWindow) // 断言SaveGame是否被调用、错误是否正确处理 // 测试取消保存的情况 gm.handleSaveConfirm(false, mockWindow) // 断言SaveGame未被调用 }
2. 用Fyne测试包模拟对话框交互
Fyne的test包提供了查找当前窗口对话框的方法,可直接获取ConfirmDialog实例,模拟点击确认或取消按钮。
修改后的测试代码:
func TestSaveButtonWithDialog(t *testing.T) { tw := test.NewWindow(nil) gm := GunMenu{ SaveData: &MockSaveData{}, // 注入Mock的SaveData } gm.LoadButton(&tw) // 触发按钮点击,弹出对话框 test.Tap(gm.SaveButton) // 查找当前窗口中的ConfirmDialog d := test.WinFindDialog(tw).(*dialog.ConfirmDialog) if d == nil { t.Fatal("未找到ConfirmDialog") } // 模拟点击确认按钮 test.Tap(d.Affirm) // 断言SaveGame是否被调用 // 再次触发按钮,测试取消逻辑 test.Tap(gm.SaveButton) d = test.WinFindDialog(tw).(*dialog.ConfirmDialog) test.Tap(d.Negative) // 断言SaveGame未被调用 }
3. 依赖注入对话框服务,解耦UI与业务
定义对话框服务接口,生产环境用Fyne实现,测试时用Mock实现,完全控制对话框的回调触发,无需实际渲染UI。
定义接口与实现:
type DialogService interface { ShowConfirm(title, message string, callback func(bool), parent fyne.Window) } // 生产环境实现 type FyneDialogService struct{} func (f *FyneDialogService) ShowConfirm(title, message string, callback func(bool), parent fyne.Window) { dialog.ShowConfirm(title, message, callback, parent) } // 测试用Mock实现 type MockDialogService struct { ShowConfirmCalled bool LastCallback func(bool) } func (m *MockDialogService) ShowConfirm(title, message string, callback func(bool), parent fyne.Window) { m.ShowConfirmCalled = true m.LastCallback = callback }
修改GunMenu代码,注入DialogService:
type GunMenu struct { SaveButton *widget.Button SaveData SaveDataInterface DialogService DialogService // 注入对话框服务 } func (g *GunMenu) LoadButton(w *fyne.Window) (sb *widget.Button) { g.SaveButton = widget.NewButton("save", func() { fmt.Println("saving...") g.DialogService.ShowConfirm("Saving...", "overwrite save?", func(b bool) { err := g.SaveData.SaveGame() if err != nil { dialog.NewError(err, *w) } }, *w) }) return g.SaveButton }
测试代码:
func TestSaveButtonWithMockDialog(t *testing.T) { tw := test.NewWindow(nil) mockDialog := &MockDialogService{} mockSaveData := &MockSaveData{} gm := &GunMenu{ SaveData: mockSaveData, DialogService: mockDialog, } gm.LoadButton(&tw) // 触发按钮点击 test.Tap(gm.SaveButton) // 断言对话框服务被调用 if !mockDialog.ShowConfirmCalled { t.Fatal("对话框服务未被调用") } // 模拟用户点击确认 mockDialog.LastCallback(true) // 断言SaveGame被调用 // 模拟用户点击取消 mockDialog.LastCallback(false) // 断言SaveGame未被调用 }
内容的提问来源于stack exchange,提问作者Dart Feld
相关产品推荐
相关产品推荐

