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);
}
}
}
package p111;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class Yahoo_c {
public static void main(String[] args) {
// TODO Auto-generated method stub
WebDriver wi = new FirefoxDriver();
wi.get("https://in.yahoo.com/?p=us");
wi.findElement(By.xpath("//[#id='UHSearchBox']")).sendKeys("pizza");
try {
Thread.sleep(50);
} catch (Exception e) {
System.out.println("wait ended");
}
String sl = wi.findElement(By.cssSelector("[id^='yui_3_12_0_1_14']")).getText();
System.out.println(sl);
}
}
Above is the code.
When i run this, execution goes until "pizza" being entered into yahoo search.Later with no error message in console execution terminates.
The error image is
Please help resolve this issue.Am trying to select pizza delivery from list.
You can try Name instead of path as the yahoo search has a name for selenium to work with. Please let know if Xpath is must for you to work then i will change my code.
public static void main(String [] arg){
WebDriver wi = new FirefoxDriver();
wi.get("https://in.yahoo.com/?p=us");
WebElement yahooSearch= wi.findElement(By.name("p"));
yahooSearch.sendKeys("pizza");
try {
Thread.sleep(50);
} catch (Exception e) {
System.out.println("wait ended");
}
String sl = wi.findElement(By.cssSelector("[id^='yui_3_12_0_1_14']")).getText();
System.out.println(sl);
}
}
Or you can use the same by ID
public static void main(String [] arg){
WebDriver wi = new FirefoxDriver();
wi.get("https://in.yahoo.com/?p=us");
WebElement yahooSearch= wi.findElement(By.id("UHSearchBox"));
yahooSearch.sendKeys("pizza");
try {
Thread.sleep(50);
} catch (Exception e) {
System.out.println("wait ended");
}
String sl = wi.findElement(By.cssSelector("[id^='yui_3_12_0_1_14']")).getText();
System.out.println(sl);
}
The option you are trying to click is a link in <a> anchor tag.. you can simply use By.linkText if you are specific on the link.
driver.findElement(By.linkText("pizza delivery")).click();
Problem is that you are sendKeys, even though the pizza is typed but the drop-down list does not appears because sendKeys is not equivalent of typing through keyboard. Work around is simple. You need to perform a keyboard action after writing "pizza".
// type pizza
wi.findElement(By.xpath("//[#id='UHSearchBox']")).sendKeys("pizza");
// now perform keyboard action (of pressing space key)
wi.findElement(By.xpath("String")).SendKeys(Keys.Space);
// now click on the pizza delivery link
wi.findElement(By.linkText("pizza delivery")).click();
Try above code in your project, after adding proper wait and with correct element locators.
Try this xpath //*[contains(text(),'pizza delivery')]
It'll work! :)
Check this in firepath and make sure you get only one node with the locator.
I've created Keyboard class for working with Robot. But when I starting using methods from this class I have error in logs.
Here's my Keyboard.java with method pressEscape():
public class Keyboard {
private static Robot robot;
private static int time = 1000;
public Keyboard(){
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
}
public void pressEscape() throws TestException {
if (!getSession().CanRun())
throw new TestException(InvalidStateMessage);
robot.delay(time);
robot.keyPress(KeyEvent.VK_ESCAPE);
robot.delay(time);
robot.keyRelease(KeyEvent.VK_ESCAPE);
robot.delay(time);
}
}
And here's error from log:
[2015/08/13 15:43:29] [ID:7F4315A] [Selenium]: java.lang.NullPointerException
[2015/08/13 15:43:29] [ID:7F4315A] [Selenium]: at Kodak.AutoTest.Framework.Keyboard.pressEscape(Keyboard.java:128)
Change a little method:
public void pressEscape() throws TestException {
if (!getSession().CanRun())
throw new TestException(InvalidStateMessage);
try {
new Robot();
robot.setAutoDelay(time);
robot.keyPress(KeyEvent.VK_ESCAPE);
robot.setAutoDelay(time);
robot.keyRelease(KeyEvent.VK_ESCAPE);
} catch (AWTException e) {
e.printStackTrace();
}
}
but still has the same error on line:
robot.keyPress(KeyEvent.VK_ESCAPE);
It looks as though the only place you can realistically get a null pointer exception in that method is in this line:
if (!getSession().CanRun())
which will throw a NPE if getSession() returns null. Actually the other option is if you failed to initialise robot, but then you'd be seeing an AWTException in the logs.
But you should confirm this by looking at the line number that the logs gave you. If this is right, I'd suggest changing
if (!getSession().CanRun())
throw new TestException(InvalidStateMessage);
to
Session session = getSession();
if ((session==null) || !session.CanRun())
throw new TestException(InvalidStateMessage);
That will harden that part, and mean you'll get a TestException if the session is null. (I am guessing the type that getSession() returns.)
Additionally, although you are initialising robot in your constructor, you should be aware that it's a static field. That means that if you have lots of Keyboard instances, every time you create an instance, it'll overwrite the previous instance of robot with a new one, because they all share the same one.
You should either change it to an instance field (remove the static modifier), or initialise it only once (check if it's null, and initialise it only if so).
For this to happen your robot variable should be null at line 128. This can happen only new Robot() threw an exception. Check your logs to see whether it is the case. And also see comments to your questions from MadProgrammer and act on them.
It's working now, here's full code:
public class Keyboard {
private static Robot robot;
public void pressEscape() throws TestException {
if (!getSession().CanRun())
throw new TestException(InvalidStateMessage);
try {
robot = new Robot();
robot.setAutoDelay(time);
robot.keyPress(KeyEvent.VK_ESCAPE);
robot.setAutoDelay(time);
robot.keyRelease(KeyEvent.VK_ESCAPE);
} catch (AWTException e) {
e.printStackTrace();
}
}
}
I am currently using loops with the finch robot to test some java codes and have come across an error. Here is my code.
import edu.cmu.ri.createlab.terk.robot.finch.Finch;
public class CS1702_Lab4 {
public static void main (String[] args) throws Exception
{
Finch myf = new Finch();
myf.setWheelVelocities(100,100);
long before = System.currentTimeMillis();{
while(System.currentTimeMillis() - before < 5000)
{
Thread.sleep(500);
if (myf.isTapped()) break;
}
myf.stopWheels();
myf.quit();
}
}
On line "myf.setWheelVelocities(100,100)", I am getting the following error;
Syntax error on tokens, delete these tokens
-Syntax error on token(s), misplaced construct(s).
Any help towards solving this error is appreciated. Many thanks.
you have too many brackets { }
remove them in these lines:
long before = System.currentTimeMillis();{
and here:
myf.quit();
}
Also it seems as if you have no class declaration.
public class CS1702_Lab4 {
public static void main (String[] args) throws Exception
{
Finch myf = new Finch();
myf.setWheelVelocities(100,100);
long before = System.currentTimeMillis();
while(System.currentTimeMillis() - before < 5000)
{
Thread.sleep(500);
if (myf.isTapped()) break;
}
myf.stopWheels();
myf.quit();
}
}
It might be too late now, but this piece of code
myf.setWheelVelocities(100,100);
should have 3 sets of numbers e.g.
myf.setWheelVelocities(100,100,5000);
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
public class Images {
try {
public static Image button = new Image("images/buttons/0/Button.png");
} catch(SlickException e) {
e.printStackTrace();
}
}
This just gives an error, I want to be able to store some images in static variables and access them from another class without instantiating it.
I could possibly make a method to initialise all them and set values to them, but then the variables wouldn't be static.
The reason I need TryCatch is because the constructor of the Image class throws a SlickException
Two options:
Use a static initializer block
public static final Image button;
static {
try/catch in here, assign to button
}
Use a method for initialization
public static final Image button = createButton();
private static Image createButton() {
try/catch in here
}
Personally I'm somewhat skeptical of this being a good idea though - making type initialization do "real work" can lead to bugs which are hard to track down. Is all the referring code really set up for it to be null in the case of problems?
You can put your exception handling code in a static block.
public static Image button;
static {
try {
button = new Image("images/buttons/0/Button.png");
} catch(SlickException e) {
e.printStackTrace();
}
}
Just declare the static variable outside the block..and it will work..
public static final Image button = setImageButt();
public static Image setImageButt(){
try {
button = new Image("images/buttons/0/Button.png");
} catch(SlickException e) {
e.printStackTrace();
}
}
try to place it into a static block
public static Image button;
static{
try {
button = new Image("images/buttons/0/Button.png");
} catch(SlickException e) {
e.printStackTrace();
}
}
You should use static constructor.
public class Images {
public static Image button;
static{
try {
button = new Image("images/buttons/0/Button.png");
} catch(SlickException e) {
e.printStackTrace();
}
}
}
Use a static block for initialization and take extra care to not swallow the exception:
public class Images {
public final static Image BUTTON;
static {
Image i;
try {
i = new Image("images/buttons/0/Button.png");
} catch (SlickException e) {
throw new ExceptionInInitializerError(e);
}
BUTTON = i;
}
}
I corrected some things from your code:
Creating the image in initializer implies it should be a constant, thus final.
Constants should have uppercase names, thus BUTTON.
Exceptions during initialization should not be swallowed but properly indicated. Otherwise you can spend hours debugging the image not being found because there will be no indication initialization failed. ExceptionInInitializerError is the standard exception to throw in that case.
There are other solutions, but this is the cleanest IMO.
I have faced similar problem once and while for finding the solution I just came across to this question. I have solved similar problem just putting all of the code in the default constructor.
Your solution code will be like this:
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
public class Images {
public Images{
try {
public static Image button = new Image("images/buttons/0/Button.png");
} catch(SlickException e) {
e.printStackTrace();
}
}
}