自定义JDialog子类的销毁单元测试失败原因及解决方法咨询
自定义JDialog子类的销毁单元测试失败原因及解决方法咨询
嘿,我来帮你分析下这个测试失败的原因,再给你几个可行的解决办法!
首先说为啥测试通不过:你创建的dialogSpy是原始dialog对象的代理,但OK按钮的点击事件是在原始dialog初始化阶段就绑定好了的——当时按钮的ActionListener里调用的this.dispose(),这里的this指的是最初的SimpleDialog实例,可不是你后来创建的dialogSpy代理。所以当你点击按钮时,实际是原始对象调用了dispose(),完全没经过spy,Mockito自然捕获不到这个调用,测试也就失败了。
接下来是具体的解决办法:
解决办法1:手动让Spy接管按钮事件
这种方法不用修改业务代码,直接在测试里替换按钮的ActionListener,让它调用spy的dispose():
@Test void ifOkPressed_dialogDisposed() { SimpleDialog dialog = new SimpleDialog(); SimpleDialog dialogSpy = spy(dialog); JButton okButton = dialogSpy.getOkButton(); // 先移除原始的ActionListener,避免触发原始对象的dispose for (ActionListener listener : okButton.getActionListeners()) { okButton.removeActionListener(listener); } // 添加新的ActionListener,明确调用spy的dispose okButton.addActionListener(e -> dialogSpy.dispose()); okButton.doClick(); then(dialogSpy).should().dispose(); }
解决办法2:重构业务代码,提升可测试性
更优雅的方式是把销毁逻辑抽成独立方法,这样测试起来更顺畅:
先修改SimpleDialog类:
public class SimpleDialog extends JDialog { private JButton okButton; public SimpleDialog() { initUI(); } private void initUI() { JPanel panel = new JPanel(new FlowLayout()); okButton = new JButton("OK"); // 调用抽出来的关闭方法,而不是直接调用dispose okButton.addActionListener(e -> closeDialog()); panel.add(okButton); this.add(panel); this.setSize(new Dimension(300, 200)); } // 把销毁逻辑封装成保护方法,方便测试时拦截 protected void closeDialog() { this.dispose(); } public JButton getOkButton() { return okButton; } }
然后测试代码就可以直接验证spy的方法调用了:
@Test void ifOkPressed_dialogDisposed() { SimpleDialog dialogSpy = spy(new SimpleDialog()); JButton okButton = dialogSpy.getOkButton(); okButton.doClick(); // 既可以验证closeDialog被调用 then(dialogSpy).should().closeDialog(); // 也可以直接验证dispose被调用 then(dialogSpy).should().dispose(); }
解决办法3:构造注入ActionListener(解耦测试)
如果想让测试更灵活,还可以把按钮的事件逻辑通过构造方法注入,彻底解耦业务代码和测试:
修改SimpleDialog类:
public class SimpleDialog extends JDialog { private JButton okButton; // 供测试使用的构造方法,传入自定义ActionListener public SimpleDialog(ActionListener okActionListener) { initUI(okActionListener); } // 原无参构造,使用默认的销毁逻辑 public SimpleDialog() { this(e -> dispose()); } private void initUI(ActionListener okActionListener) { JPanel panel = new JPanel(new FlowLayout()); okButton = new JButton("OK"); okButton.addActionListener(okActionListener); panel.add(okButton); this.add(panel); this.setSize(new Dimension(300, 200)); } public JButton getOkButton() { return okButton; } }
测试时可以直接用mock的ActionListener验证,或者继续用spy验证dispose:
@Test void ifOkPressed_dialogDisposed() { // 方式1:用mock的ActionListener验证点击事件触发 ActionListener mockListener = mock(ActionListener.class); SimpleDialog dialog = new SimpleDialog(mockListener); JButton okButton = dialog.getOkButton(); okButton.doClick(); then(mockListener).should().actionPerformed(any(ActionEvent.class)); // 方式2:继续用spy验证dispose SimpleDialog dialogSpy = spy(new SimpleDialog()); JButton okBtn = dialogSpy.getOkButton(); okBtn.doClick(); then(dialogSpy).should().dispose(); }
备注:内容来源于stack exchange,提问作者Cagepi
相关产品推荐
相关产品推荐

