如何在测试时自动点击JOptionPane上的OK?

时间:2014-03-14 22:45:44

标签: java swing testing popup joptionpane

在我的程序中,我正在测试一个按钮,单击该按钮会在应用程序中抛出一个JOptionPane窗口。

然而,当弹出此窗口时,它希望用户单击“确定”,这意味着除非手动关闭窗口,否则测试无法继续。

我希望能够在我的测试中出现时以编程方式关闭此窗口,或者阻止这些JOptionPane窗口完全弹出以进行此测试。

3 个答案:

答案 0 :(得分:2)

在这种情况下,我通常做的是确保UI不会出现在我的测试中。只是用一些代码来说明这一点。让我们说对话框是在Action

的某处触发的
public class ActionWithUI extends AbstractAction{

  @Override
  public void actionPerformed( ActionEvent e ){
    //... do some stuff
   int result = JOptionPane.show... //show the dialog
   //do some other stuff based on the result of the JOptionPane
  }
}

然后我会重构我的代码,以便JOptionPane的显示在它自己的单独方法中。

public class ActionWithUI extends AbstractAction{

  @Override
  public void actionPerformed( ActionEvent e ){
    //... do some stuff
   int result = askUserForInput();
   //do some other stuff based on the result of the JOptionPane
  }

  int askUserForInput(){
    return JOptionPane.show...
  }
}

现在在我的测试中,我可以测试该操作的自定义版本,其中我覆盖askUserForInput方法并返回"确定" (或"取消",...)选项。这将避免我的测试中任何聪明的技巧,并保持我的代码可读。

我有时使用的另一种方法是不直接调用JOptionPane.show,而是确保我的类在其构造函数中接受用于显示此类对话框的委托类。在我的测试中,我可以插入一个模拟而不是真正的类,并拦截对UI的调用。

当然,这两种方法实际上都没有测试JOptionPane是否正确显示。但看到这是一个JDK方法,我真的觉得不需要测试它。并不是说我绕过了一些业务逻辑。我刚刚摆脱了JOptionPane.show电话。

如果这些都不是选项,我通常会使用附加到DefaultKeyboardFocusManager的监听器。当具有焦点的组件发生变化时,我会看到它是JOptionPane(使用Swing层次结构)并将其处理掉。结果很好,但仍然不是100%可靠。因此(当然对于新代码而言),我试图坚持以前概述的两种方法之一

答案 1 :(得分:1)

有两种基本方法可以解决这个问题。两者都要求您能够找到按钮。

这将需要能够使用第二个Thread找到窗口/对话框并走它的容器来找到按钮......

取决于您的测试设置方式取决于您可能需要经历多少额外工作。 UI应该在Event Dispatching Thread中运行,如果您的测试在同一个线程中运行,那么您将需要使用单独的Thread,因为JOptionPane将阻止EDT(以特殊方式),使代码无法运行,直到选项窗格关闭。

如果您的测试已在单独的Thread中运行,那么您可以获得更多功能......

这是一个非常简单的例子。它在EDT的上下文中初始化JOptionPane,但是在主线程中运行的方法可以找到并执行" OK"消息对话框的按钮。

import java.awt.Button;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Window;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class FindButton {

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JOptionPane.showMessageDialog(null, "This is message dialog", "Message", JOptionPane.INFORMATION_MESSAGE);
            }
        });

        JDialog frame = waitForDialog("Message");
        System.out.println("Found window " + frame);
        if (frame != null) {
            final JButton btn = getButton(frame, "OK");
            System.out.println("Found button " + btn);
            if (btn != null) {
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        btn.doClick();
                    }
                });
            }
        }
    }

    public static JDialog waitForDialog(String title) {

        JDialog win = null;
        do {
            for (Window window : Frame.getWindows()) {
                if (window instanceof JDialog) {
                    JDialog dialog = (JDialog) window;
                    System.out.println(dialog.getTitle());
                    if (title.equals(dialog.getTitle())) {
                        win = dialog;
                        break;
                    }
                }
            }

            if (win == null) {
                try {
                    Thread.sleep(250);
                } catch (InterruptedException ex) {
                    break;
                }
            }

        } while (win == null);

        return win;

    }

    public static JButton getButton(Container container, String text) {
        JButton btn = null;
        List<Container> children = new ArrayList<Container>(25);
        for (Component child : container.getComponents()) {
            System.out.println(child);
            if (child instanceof JButton) {
                JButton button = (JButton) child;
                if (text.equals(button.getText())) {
                    btn = button;
                    break;
                }
            } else if (child instanceof Container) {
                children.add((Container) child);
            }
        }
        if (btn == null) {
            for (Container cont : children) {
                JButton button = getButton(cont, text);
                if (button != null) {
                    btn = button;
                    break;
                }
            }
        }
        return btn;
    }

}

此示例使用doClick的{​​{1}}方法模拟按钮的单击。您可以使用按钮中的JButton信息和locationOnScreen来物理点击按钮。但这似乎更简单。

你也可以看一下Jemmy这是一个很好用的库,旨在让它更容易测试基于Swing(和JavaFX)的应用程序......

答案 2 :(得分:0)

首先,从JDialog对象创建JOptionPane。然后,创建一个timer以运行您想要的时间(例如1分钟),并在完成后处置dialog。然后,从JOptionPane对象中检索所选值,确保在计时器处理对话框时考虑未初始化的值。

    final JOptionPane pane = new JOptionPane(......., JOptionPane.OK_CANCEL_OPTION);
    final JDialog dialog = pane.createDialog(.....);
    Timer timer = new Timer(you_time, new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            dialog.dispose();
        }

    });
    timer.start();
    dialog.setVisible(true);
    dialog.dispose();
    Integer choice = (Integer) (pane.getValue() == JOptionPane.UNINITIALIZED_VALUE ? JOptionPane.OK_OPTION : pane.getValue());