Простой пример TestFX не удается
Работая с TestFX 4.0.14 в Eclipse Photon и fx9 или fx11 (не имеет значения), простой пример теста из вики TestFX дает сбой в should_click_on_button()
с
Expected: Labeled has text "clicked!"
but: was "click me!"
При взгляде на экран отображается панель и содержащаяся в ней кнопка, но мышь перемещается в другое место, поэтому кнопка никогда не нажимается и, следовательно, ее текст никогда не изменяется.
Есть идеи, что не так / как исправить?
Тестовый код (все для удобства скопировано из вики):
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!"));
}
}
Код приложения:
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);
}
}
}
Обновить:
Найдена открытая проблема в TestFX, которая может совпадать. В нем упоминается основная ошибка fx, которая может быть причиной - но, похоже, ее нет: она исправлена в fx11 (проверено, что код в отчете об основной ошибке проходит), но проблема testfx преобладает..
2 ответа
Оказалось, проблема с несколькими причинами:
- в основе лежит основная ошибка - робот не работает правильно на win (только 10?) экранах HiDPI, поэтому не все испытывают сбои
- ошибка в ядре исправлена в fx11 (в собственном коде win), так что теперь общедоступный робот fx ведет себя хорошо
- Код, связанный с роботом TestFX в последней версии (4.0.14), еще не обновлен до fx11, версия репо
- по умолчанию TestFX использует AWTRobot, который все еще не работает (по крайней мере, в контексте TestFX, пример в отчете об основной ошибке проходит)
- чтобы тесты TestFX работали надежно на экранах Win HiDPI, нам нужно принудительно использовать робот fx, установив системное свойство testfx.robot на glass
- Последнее замечание: при установке свойства программно через
System.getProperties().put("testfx.robot", "glass")
в тестовом коде это необходимо сделать до запуска тестового класса, то есть в методе, аннотированном @BeforeClass (не в @Before илиtestApp.init()
)! В противном случае самый первый тест, в котором прямо или косвенно задействуется mouseMove, будет неудачным, последующие из них будут хорошими (какое-то время я озадачен, потому что неудача кажется ложной;).
Образец:
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());
}
Меня устраивает.
В любом случае, вы можете проверять изменение пользовательского интерфейса до того, как оно произойдет, поскольку оно выполняется в потоке приложения FX, а не в потоке, выполняющем тест.
Используйте эту строку
WaitForAsyncUtils.waitForFxEvents()
между clickOn
а также verifyThat
звонки.