为什么 JPanelFixture.comboBox().pressAndReleaseKeys() 适用于 FEST,但不适用于 AssertJ?

Why does JPanelFixture.comboBox().pressAndReleaseKeys() work with FEST, but not with AssertJ?

当尝试使用 AssertJ 的 pressAndReleaseKeys() 模拟输入以在 Java Swing 程序中对 JComboBox 进行单元测试时,我没有看到预期的行为。该程序通常会挂在 pressAndReleaseKeys 行然后失败,或者偶尔会删除当前正在测试的 JComboBox 中的所有文本,从而导致以后的断言失败(即 requireSelection())。当它挂起时,我收到的所提供示例程序(见下文)的堆栈跟踪如下:

Focus change to javax.swing.JComboBox[name='combob', selectedItem='Bean', contents=["Pork", "Beans", "Rice"], editable=true, enabled=true, visible=true, showing=true] failed focus owner: javax.swing.plaf.metal.MetalComboBoxEditor(javax.swing.JTextField)[name=null, text='Bean', enabled=true, visible=true, showing=true]

org.assertj.swing.exception.ActionFailedException
at org.assertj.swing.exception.ActionFailedException.actionFailure(ActionFailedException.java:33)
at org.assertj.swing.core.BasicRobot.focus(BasicRobot.java:301)
at org.assertj.swing.core.BasicRobot.focusAndWaitForFocusGain(BasicRobot.java:270)
at org.assertj.swing.driver.ComponentDriver.focusAndWaitForFocusGain(ComponentDriver.java:419)
at org.assertj.swing.driver.ComponentDriver.pressAndReleaseKeys(ComponentDriver.java:315)
at org.assertj.swing.fixture.AbstractComponentFixture.pressAndReleaseKeys(AbstractComponentFixture.java:293)
at javapractice.ComboBoxSampleTest.testMain(ComboBoxSampleTest.java:59)

我一直在使用 FEST 并希望将我的测试迁移到 AssertJ,因为它正在积极维护,而 FEST 多年未更新。我使用了 Joel Costigliola 的 migration from Fest to AssertJ 指南,但在使用 pressAndReleaseKeys() 模拟键盘输入时遇到了问题。我能够在使用 JTextComponentFixture 时模拟输入,即

window.textBox("textB").pressAndReleaseKeys(KeyEvent.VK_LEFT);

(其中 window 是一个 FrameFixture,它是 AssertJ 和 FEST 中的容器),但是我无法在使用 JComboBoxFixture 时模拟输入,即

window.comboBox("comboB").pressAndReleaseKeys(KeyEvent.VK_LEFT);

这个障碍通常是可以避免的,因为大多数“按键”都可以通过使用 enterText 来模拟,即

window.comboBox("comboB").enterText("\n"); //to press the enter key
window.comboBox("comboB").enterText("\b"); //to press the backspace key

但我希望能够在无法使用 enterText() 模拟按键的地方使用箭头键、控制键和其他键。这种失败是由于我的环境问题*、我使用它的方式的问题,还是 API 本身有缺陷?

我尝试使用 pressKey() 然后使用 releaseKey() 作为解决方法,但这对 JComboBox 也不起作用,我的程序反而挂在 pressKey() 上。话虽如此,我也无法使用 pressKey() 和 releaseKey() 来测试带有 FEST 的 JComboBox。

*环境详情:
语言版本:java版本“1.8.0_131”
平台版本(例如 .NET 3.5;请注意,这并不总是隐含在语言版本中,反之亦然)
操作系统:Red Hat Release 6.10 (Santiago)
IDE:Netbeans 8.0.2

示例 GUI 应用程序:

package javapractice;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class ComboBoxSample extends JFrame implements ItemListener{
    JPanel jp;
    JComboBox jcb;
    JLabel result;
    JLabel title;
    JTextField jtc;

    public static void main(String[] args) {
        ComboBoxSample frame = new ComboBoxSample();
    }
    
    ComboBoxSample() {
        super();
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setVisible(true);
        this.setTitle("Testing AssertJ");
        this.setLayout(new FlowLayout());
        jp = new JPanel();
        jcb = new JComboBox(new String[] {"Pork", "Beans", "Rice"});
        jcb.setEditable(true);
        jcb.setName("combob");
        jtc = new JTextField();
        jtc.setEditable(true);
        jtc.setPreferredSize(new Dimension(150, 25));
        jtc.setName("textb");
        title = new JLabel("Food: ");
        result = new JLabel("No food");
        jp.add(title);
        jp.add(jcb);
        jp.add(result);
        jp.add(jtc);
        this.add(jp);
        this.setLocationRelativeTo(null);
        jcb.addItemListener(this);
        

        this.pack();
        this.repaint();        
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        if(e.getSource() == jcb) {
            result.setText("I'm eating " + jcb.getSelectedItem());
        }
        this.pack();
    }
    
    public void cleanUp() {
        jcb = null;
        result = null;
        jtc = null;
        jp = null;
        title = null;
    }   
}

Fest 测试文件:

package javapractice;

import com.sun.glass.events.KeyEvent;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Fest imports.
 */
import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.fixture.FrameFixture;

public class ComboBoxSampleTest {
    private FrameFixture window;
    private ComboBoxSample frame;
    
    @BeforeClass
    public static void setUpClass() {
        FailOnThreadViolationRepaintManager.install();
    }
    
    @AfterClass
    public static void tearDownClass() {
        
    }
    
    @Before
    public void setUp() {
        frame = GuiActionRunner.execute(new GuiQuery<ComboBoxSample>() {
            @Override
            protected ComboBoxSample executeInEDT() {
                return new ComboBoxSample();
            }
        });
        window = new FrameFixture(frame);
        window.show();
    }
    
    @After
    public void tearDown() {
        window.cleanUp();
        frame.cleanUp();
    }

    /**
     * Test of main method, of class ComboBoxSample.
     */
    @Test
    public void testMain() {
        //Delay so that we can see what's going on
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ie) {
            
        }
        
        window.textBox("textb").enterText("hi there");
        window.textBox("textb").pressAndReleaseKeys(KeyEvent.VK_BACKSPACE);
        window.comboBox().replaceText("Bean");
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_S);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_DOWN);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_DOWN);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_DOWN);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_ENTER);
    }
}

AssertJ 的测试文件:

package javapractice;

import com.sun.glass.events.KeyEvent;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * AssertJ imports.
 */
import org.assertj.swing.edt.FailOnThreadViolationRepaintManager;
import org.assertj.swing.edt.GuiActionRunner;
import org.assertj.swing.fixture.FrameFixture;

public class ComboBoxSampleTest {
    private FrameFixture window;
    private ComboBoxSample frame;
    
    @BeforeClass
    public static void setUpClass() {
        FailOnThreadViolationRepaintManager.install();
    }
    
    @AfterClass
    public static void tearDownClass() {
        
    }
    
    @Before
    public void setUp() {
        frame = GuiActionRunner.execute(() -> new ComboBoxSample());
        window = new FrameFixture(frame);
        window.show();
    }
    
    @After
    public void tearDown() {
        window.cleanUp();
        frame.cleanUp();
    }

    /**
     * Test of main method, of class ComboBoxSample.
     */
    @Test
    public void testMain() {
        //Delay so that we can see what's going on
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ie) {
            
        }
        
        window.textBox("textb").enterText("hi there");
        window.textBox("textb").pressAndReleaseKeys(KeyEvent.VK_BACKSPACE);
        window.comboBox().replaceText("Bean");
        //the above line is the last one to execute
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_S);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_DOWN);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_DOWN);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_DOWN);
        window.comboBox().pressAndReleaseKeys(KeyEvent.VK_ENTER);
    }
}

这不是问题的答案,而是允许所需行为的解决方法。可以通过为 comboBox() 调用 robot() 来缓解此问题。

而不是

window.comboBox().pressAndReleaseKeys(KeyEvent.VK_S);

尝试做

window.comboBox().robot().pressAndReleaseKeys(KeyEvent.VK_S);