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

自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 14:33:04