I am completely stuck in a java test; it's about sending by the test method the character 'a' to the JTextField of a JFrame component.
The JFrame class implements the KeyListener interface, and as such overrides KeyPressed, KeyTyped, and KeyReleased. Along with this, I transfer all the keypressed of the JTextField to the JFrame; inside the JFrame constructor I have :
JTextField txf_version = new JTextField();
txf_version.addKeyListener(this);
I would like to test this behavior and then to simulate the action of type a character in the JTextField.
all my attempts failed; I tried with the java.awt.Robot class, like this : hava a look at this other post in stack overflow, but I get a strange behavior : calling
robot.keyPress(KeyEvent.VK_A);
displays the character in my IDE directly, not in the virtual JFrame! try to play with requestFocus() or requestFocusInWindow() is ineffective.
I also tried with KeyEvents:
KeyEvent key = new KeyEvent(bookWindow.txf_version, KeyEvent.KEY_PRESSED, System
.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, 'a');
bookWindow.txf_version.dispatchEvent(key);
but again the textfield's text property is not changed...
here is the method I have for now:
@Test
void testBtnSaveChangesBecomesRedWhenVersionChanged() throws AWTException,
InterruptedException, NoSuchFieldException, IllegalAccessException {
initTest();
KeyEvent key = new KeyEvent(bookWindow.txf_version, KeyEvent.KEY_PRESSED, System
.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, 'a');
bookWindow.txf_version.dispatchEvent(key);
System.out.println("dans txf_version : " + bookWindow.txf_version.getText
());
assertEquals(Color.RED, bookWindow.getBtnSaveChangesForegroundColor());
}
I can have a look at the actual behavior by writing a main() method in the JFrame's child class, but I think it is useful to know how to simulate keys for swing components testing.
thank you
EDIT:
I changed the code of my test according to AJNeufeld's answer, but it still doesn't work. Here is my test code :
@Test
void testBtnSaveChangesBecomesRedWhenVersionChanged() throws AWTException,
InterruptedException, NoSuchFieldException, IllegalAccessException,
InvocationTargetException {
//bookEditor2 & bookWindow
SwingUtilities.invokeAndWait(() -> {
bookWindow = new BookWindow();
VectorPerso two = new VectorPerso();
two.add(le_livre_de_la_jungle);
two.add(elogeMaths);
bookWindow.setTableDatas(two);
bookWindow.table.setRowSelectionInterval(1, 1);
bookWindow.txf_version.requestFocusInWindow();
KeyEvent key = new KeyEvent(bookWindow.txf_version, KeyEvent.KEY_TYPED, System
.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, 'a');
bookWindow.txf_version.dispatchEvent(key);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("dans txf_version : " + bookWindow.txf_version.getText
());
assertEquals(Color.RED, bookWindow.getBtnSaveChangesForegroundColor());
});
}
the plintln line produces a text in the console : "dans txf_version : 0", which indicates the key isn't send to the txf_version.
EDIT 2:
new try:
@Test
void testBtnSaveChangesBecomesRedWhenVersionChanged() throws AWTException,
InterruptedException, NoSuchFieldException, IllegalAccessException,
InvocationTargetException {
//bookEditor2 & bookWindow
SwingUtilities.invokeAndWait(() -> {
bookWindow = new BookWindow();
VectorPerso two = new VectorPerso();
two.add(le_livre_de_la_jungle);
two.add(elogeMaths);
bookWindow.setTableDatas(two);
bookWindow.table.setRowSelectionInterval(1, 1);
bookWindow.txf_version.requestFocusInWindow();
KeyEvent key = new KeyEvent(bookWindow.txf_version, KeyEvent.KEY_TYPED, System
.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, 'a');
bookWindow.txf_version.dispatchEvent(key);
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeAndWait(() -> {
System.out.println("dans txf_version : " + bookWindow.txf_version.getText
());
assertEquals(Color.RED, bookWindow.getBtnSaveChangesForegroundColor());
});
}
I think you're doing a couple of things wrong, but without a complete example, it is hard to tell.
First, the JTextField
is not really concerned with KEY_PRESSED
events. It is concerned with the KEY_TYPED
events.
Second, Swing processes events on the Event Dispatching Thread (EDT), which is not necessarily the thread that JUnit is going to be running on. You really shouldn't be changing things when you're not on the EDT. I'm not certain eventDispatch()
does the switch to the EDT or not. It might. But it might also do it using invokeLater()
, in which case the execution immediately passes to the assertEquals()
, which fails, because the event processing hasn't happened yet.
Here is minimal, complete, verifiable example, which shows a keypress sent, which changes the button colour, and a JUnit test case which checks it and passes:
First, the code under test:
public class SwingUnitTesting extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(SwingUnitTesting::new);
}
JTextField tf = new JTextField();
JButton btn = new JButton("Test Button");
SwingUnitTesting() {
add(tf, BorderLayout.PAGE_END);
add(btn, BorderLayout.PAGE_START);
tf.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
btn.setForeground(Color.RED);
}
});
setSize(200, 80);
setLocationByPlatform(true);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
}
}
And the unit test:
public class SwingUnitTestingTest {
SwingUnitTesting sut;
@Before
public void setUp() throws Exception {
SwingUtilities.invokeAndWait(() -> {
sut = new SwingUnitTesting();
});
}
@Test
public void btnNotRedBeforeKeypress() throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(() -> {
assertNotEquals(Color.RED, sut.btn.getForeground());
});
}
@Test
public void btnRedAfterKeypress() throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(() -> {
sut.tf.requestFocusInWindow();
sut.tf.dispatchEvent(new KeyEvent(sut.tf,
KeyEvent.KEY_TYPED, System.currentTimeMillis(), 0,
KeyEvent.VK_UNDEFINED, 'A'));
assertEquals(Color.RED, sut.btn.getForeground());
});
}
}
You can probably use some JUnit @Rule
trickery or a custom runner to automatically change to the EDT when running swing tests.
Update:
I got curious, and tried to find an existing @Rule
which puts the @Before
, @Test
, and @After
code on to the EDT, but my Google-fu failed me;
I know I've seen it before, but I couldn't find it.
In the end, I created my own:
public class EDTRule implements TestRule {
@Override
public Statement apply(Statement stmt, Description dscr) {
return new Statement() {
private Throwable ex;
@Override
public void evaluate() throws Throwable {
SwingUtilities.invokeAndWait(() -> {
try {
stmt.evaluate();
} catch (Throwable t) {
ex = t;
}
});
if (ex != null) {
throw ex;
}
}
};
}
}
Using this rule, the JUnit test becomes a little simpler:
public class SwingUnitTestingTest {
@Rule
public TestRule edt = new EDTRule();
SwingUnitTesting sut;
@Before
public void setUp() throws Exception {
sut = new SwingUnitTesting();
}
@Test
public void btnNotRedBeforeKeypress() {
assertNotEquals(Color.RED, sut.btn.getForeground());
}
@Test
public void btnRedAfterKeypress() {
sut.tf.requestFocusInWindow();
sut.tf.dispatchEvent(
new KeyEvent(sut.tf, KeyEvent.KEY_TYPED, System.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, 'A'));
assertEquals(Color.RED, sut.btn.getForeground());
}
}
Tested on MacOS, with jdk1.8.0_121
Yo, I guess you have two choices
1) It's still to find a solution using SWING (but in this case, I have no experience and any idead how to help you).
2) It's to use Sikulli java framework for testing desktop app. You can add the dependency then
- make screenshot of your UI elements, and put them into test data folder of your app
Using sikuli + JUnit
- write simple test where you set a path for your pic of button (for example)
and write action, move, click or what actually you need.
in very simple that will be looks like
Button button = new Button("test-folder/pic1.jpg");
button.click();
After run, you will see that, your cursor was move on button, and click by it.
For more details, find examples in web about Sikulli + Java.
recently I had to test a customized KeyAdapter and I created a customized JTextField to dispatch key events. A piece of the code I used is bellow:
public class ProcessKeyOnTextFieldTest {
@Test
public void pressKeyTest() throws Exception {
JTextFieldWithTypedKeySupport textField = new JTextFieldWithTypedKeySupport();
textField.pressKey('a');
textField.pressKey('b');
assertEquals("ab", textField.getText());
}
class JTextFieldWithTypedKeySupport extends JTextField {
int timestamp;
void pressKey(char key) throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(() -> super.processKeyEvent(createEvent(key)));
}
KeyEvent createEvent(char keyChar) {
return new KeyEvent(this, KeyEvent.KEY_TYPED, timestamp++, 0, KeyEvent.VK_UNDEFINED, keyChar);
}
}
}