Search code examples
javajavafxtestfx

Simple TestFX example fails


Working with TestFX 4.0.14 in Eclipse photon and fx9 or fx11 (doesn't matter), the simple example test from the TestFX wiki fails in should_click_on_button() with

Expected: Labeled has text "clicked!"
         but: was "click me!"

When looking at the screen, the pane and its contained button is shown, but the mouse moves to someplace else: so the button is never clicked and consequently its text never changed.

Any idea what's wrong/how to fix?

The test code (all copied from the wiki for convenience):

import org.junit.Test;
import org.testfx.framework.junit.ApplicationTest;

import static org.testfx.api.FxAssert.*;
import static org.testfx.matcher.control.LabeledMatchers.*;

import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 * Simple testfx example from testfx wiki:
 * https://github.com/TestFX/TestFX/wiki/Getting-Started
 * 
 */
public class ClickApplicationTest extends ApplicationTest {
    @Override
    public void start(Stage stage) {
        Parent sceneRoot = new ClickApplication.ClickPane();
        Scene scene = new Scene(sceneRoot, 100, 100);
        stage.setScene(scene);
        stage.show();
    }

    @Test
    public void should_contain_button() {
        // expect:
        verifyThat(".button", hasText("click me!"));
    }

    @Test
    public void should_click_on_button() {
        // when:
        clickOn(".button");

        // then:
        verifyThat(".button", hasText("clicked!"));
    }


}

The application code:

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
 * Simple testfx example from testfx wiki:
 * https://github.com/TestFX/TestFX/wiki/Getting-Started
 * 
 */
public class ClickApplication extends Application {
    // application for acceptance tests.
    @Override public void start(Stage stage) {
        Parent sceneRoot = new ClickPane();
        Scene scene = new Scene(sceneRoot, 100, 100);
        stage.setScene(scene);
        stage.show();
    }

    // scene object for unit tests
    public static class ClickPane extends StackPane {
        public ClickPane() {
            super();
            Button button = new Button("click me!");
            button.setOnAction(actionEvent -> button.setText("clicked!"));
            getChildren().add(button);
        }
    }
}

Update:

Found an open issue in TestFX that might match. It mentions a core fx bug that might be the reason - but doesn't seem to be: it's fixed in fx11 (verified that the code in the core bug report passes), but the testfx issue prevails ..


Solution

  • Turned out to be a problem with several reasons:

    • at the very base is a core bug - robot doesn't move correctly on win (10 only?) HiDPI screens, so not everybody experiences the failure
    • the core bug is fixed in fx11 (in native win code), so that the now public fx Robot behaves well
    • TestFX robot-related code in the latest release (4.0.14) is not yet updated to fx11, the repo version is
    • by default, TestFX uses the AWTRobot which still fails (at least in the context of TestFX, the example in the core bug report passes)
    • to make TestFX tests run reliable on win HiDPI screens, we need to force the usage of the fx robot, done by setting a system property testfx.robot to glass
    • one last quirk: when setting the property programmatically via System.getProperties().put("testfx.robot", "glass") in the testing code, it is mandatory to do this before the tests class starts, that is in a method annotated by @BeforeClass (not in @Before nor testApp.init())! Otherwise the very first test that directly or indirectly involves a mouseMove will fail, subsequent ones are fine (puzzled me a while because the failure feels spurious;).

    Sample:

    public class ClickTest extends ApplicationTest {
    
        @Test
        public void testClick() {
            verifyThat(".button", hasText("click me!"));
            clickOn(".button");
            verifyThat(".button", hasText("clicked!"));
        }
    
        /**
         * Use glass robot.
         * Note: this must happen once before the class is loaded. Otherwise,
         * the very first test run uses the awt robot
         */
        @BeforeClass
        public static void config() throws Exception {
            System.getProperties().put("testfx.robot", "glass");
        }
    
        @Override
        public void start(Stage stage) {
            Parent sceneRoot = new ClickPane();
            Scene scene = new Scene(sceneRoot, 100, 100);
            stage.setScene(scene);
            stage.show();
        }
    
        // scene object for unit tests
        public static class ClickPane extends StackPane {
            public ClickPane() {
                super();
                Button button = new Button("click me!");
                button.setOnAction(actionEvent -> button.setText("clicked!"));
                getChildren().add(button);
            }
        }
    
        @SuppressWarnings("unused")
        private static final Logger LOG = Logger
                .getLogger(ClickTest.class.getName());
    }