如何测试JavaFX控制器的方法?

How to test method of JavaFX controller?

我正在尝试使用 TestFX 来测试我的应用程序。我想 运行 测试我的控制器的方法。

Main.java:

public class Main extends Application {
    try{
        new Flow(ManageCtrl.class).startInStage(primaryStage);
    } catch (Exception ex) {
        LOGGER.log(Level.SEVERE, null, ex);
    }
}

ManageCtrl.java:

@ViewController("/FPManage.fxml")
public class ManageCtrl extends AnchorPane {

    @FXML // fx:id="email"
    private TextField email; // Value injected by FXMLLoader

    public void setEmail(String address) {
        this.email.setText(address);
    }
}

ManageCtrlTest.java:

public class ManageCtrlTest extends ApplicationTest {

    @Override
    public void start(Stage stage) {
        try {
            new Flow(ManageCtrl.class).startInStage(stage);
        } catch (FlowException ex) {
            Logger.getLogger(ManageCtrlTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Test
    public void testSetEmail() {
        ManageCtrl instance = new ManageCtrl();
        instance.setEmail("test@gmai.com");

        assertEquals("test@gmail.com", ((TextField)GuiTest.find("#email")).getText());
    }
}

但我得到以下异常:

testSetEmail Failed: java.lang.illegalStateException: Not on FX application thread; currentThread = Test worker
java.lang.illegalStateException: Not on FX application thread; currentThread = Test Worker

感谢您的帮助。

IllegalStateException 与 JavaFX 和 TestFX 的性质有关

ManageCtrlAnchorPane 扩展,它是 JavaFX 的 Scene 对象之一,所有这些对象都需要在 JavaFX 线程(也称为 JavaFX 应用程序线程或 JavaFX 用户线程)中构建.您可以使用 ApplicationTest#interact 在 JavaFX 线程中构造 ManageCtrl

interact(() -> {
    ManageCtrl controller = new ManageCtrl();
    controller.setEmail("test@gmail.com");
});

然而,这将引发 NullPointerException,这是由与 new Flow(ManageCtrl.class) 一起使用的 DataFX 的 特性引起的。

new Flow(ManageCtrl.class).startInStage(stage) 将使用您的 @ViewController 中定义的对象注入控制器中所有 @FXML 注释的字段 - new ManageCtrl() 不会。我们可以通过在测试前在字段controller中构造ManageCtrl来解决这个问题:

@Override
public void start(Stage stage) throws Exception {
    Flow flow = new Flow(ManageCtrl.class);

    // create a handler to initialize a view and a sceneRoot.
    FlowHandler handler = flow.createHandler();
    StackPane sceneRoot = handler.start();

    // retrieve the injected controller from the view.
    FlowView view = handler.getCurrentView();
    controller = (ManageCtrl) view.getViewContext().getController();

    // attach the sceneRoot to stage.
    stage.setScene(new Scene(sceneRoot));
    stage.show();
}

您现在可以使用以下方法测试您的控制器:

@Test
public void should_set_email() throws Exception {
    // when:
    interact(() -> {
        controller.setEmail("test@gmail.com");
    });

    // then:
    verifyThat("#email", hasText("test@gmail.com"));
}

在试图简化这方面测试的 issue on GitHub. I've also created a pull request on Bitbucket 中详细介绍了整个事情。