假设我想测试这个简单的
JButton
扩展
package app;
import solution.common.utils.MathConverter;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class JDropdownButton
extends JButton
implements ActionListener {
private JPopupMenu popupMenu;
public JDropdownButton() {
this(null, null);
}
public JDropdownButton(String text, Icon icon) {
super(text, icon);
super.addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
if (null == popupMenu || null == popupMenu.getSubElements()) {
return;
}
if (!popupMenu.isVisible()) {
Point p = getLocationOnScreen();
popupMenu.setLocation(MathConverter.doubleToInt(p.getX()),
MathConverter.doubleToInt(p.getY()) + getHeight());
popupMenu.setVisible(true);
}
}
public void setDropdownMenu(JPopupMenu menu) {
popupMenu = menu;
if (popupMenu != null) {
popupMenu.setInvoker(this);
}
}
}
此时,我只对测试单击按钮是否显示弹出窗口感兴趣。如果我运行这个
package app;
import org.junit.jupiter.api.Test;
import javax.swing.*;
import java.awt.event.ActionEvent;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class JDropdownButtonTest {
@Test
void actionPerformed_showsPopup() {
JDropdownButton dropdownButton = new JDropdownButton();
JPopupMenu popupMenu = new JPopupMenu();
dropdownButton.setDropdownMenu(popupMenu);
// JFrame frame = new JFrame();
// frame.add(dropdownButton);
// frame.setVisible(true);
assertFalse(popupMenu.isVisible());
dropdownButton.doClick();
assertTrue(popupMenu.isVisible());
}
}
我明白了
java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location
at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:2062)
at java.awt.Component.getLocationOnScreen(Component.java:2036)
at app.JDropdownButton.actionPerformed(JDropdownButton.java:50)
所以它必须要显示。由于 Mockito 无法区分真正的调用和设置调用,因此这将抛出相同的异常
@Test
void actionPerformed_showsPopup() {
JDropdownButton dropdownButton = spy(JDropdownButton.class);
given(dropdownButton.getLocationOnScreen()).willReturn(new Point(0,0));
JPopupMenu popupMenu = new JPopupMenu();
dropdownButton.setDropdownMenu(popupMenu);
// JFrame frame = new JFrame();
// frame.add(dropdownButton);
// frame.setVisible(true);
assertFalse(popupMenu.isVisible());
dropdownButton.doClick();
assertTrue(popupMenu.isVisible());
}
我可以取消注释这些行,测试运行良好,但是 TeamCity 服务器上的 CI 构建将失败,并出现“无头异常”,因为没有可用的图形环境
所以我需要一个强大的 GUI 测试,也可以在无头环境中运行
我该怎么做? 更新 CI 服务器的 Docker 镜像是唯一的解决方案吗?
这并不是真正的答案,更像是一种解决方法。
doReturn()
结构避免了实际的方法调用(我猜是因为when()
返回一个“吞噬”后续调用的模拟)
@Test
void actionPerformed_showsPopup() {
JDropdownButton dropdownButton = spy(JDropdownButton.class);
doReturn(new Point(0,0)).when(dropdownButton).getLocationOnScreen();
JPopupMenu popupMenu = new JPopupMenu();
dropdownButton.setDropdownMenu(popupMenu);
assertFalse(popupMenu.isVisible());
dropdownButton.doClick();
assertTrue(popupMenu.isVisible());
}