Java TestFx-Can';t访问登录对话框

Java TestFx-Can';t访问登录对话框,java,javafx,login,testfx,Java,Javafx,Login,Testfx,我有一个简单的javafx应用程序,在stage.show()之后,我调用login dialog。当我运行测试时,他们不会开始工作,直到登录对话框被手动填充和确认。出于测试目的,我尝试在点击stage上的一个按钮后显示另一个对话框,通过testFx操作它没有问题。唯一的问题是初始登录对话框。有没有办法解决这种行为,或者我做错了什么 测试场景: MainApp.java package cz.mono.monofx; import cz.mono.monofx.fxml.LoginDialog

我有一个简单的javafx应用程序,在stage.show()之后,我调用login dialog。当我运行测试时,他们不会开始工作,直到登录对话框被手动填充和确认。出于测试目的,我尝试在点击stage上的一个按钮后显示另一个对话框,通过testFx操作它没有问题。唯一的问题是初始登录对话框。有没有办法解决这种行为,或者我做错了什么

测试场景:

MainApp.java

package cz.mono.monofx;

import cz.mono.monofx.fxml.LoginDialog;
import cz.mono.monofx.fxml.ScreensController;
import java.util.Optional;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Pair;


public class MainApp extends Application {

    public static final String SCREEN1 = "scene";
    private final String screen1fxml = "/fxml/Scene.fxml";
    public static final String SCREEN2 = "scene2";
    private final String screen2fxml = "/fxml/Scene2.fxml";

    @Override
    public void start(Stage stage) throws Exception {

        ScreensController mainContainer = new ScreensController();
        mainContainer.loadScreen(SCREEN1, screen1fxml);
        mainContainer.loadScreen(SCREEN2, screen2fxml);

        mainContainer.setScreen(SCREEN1);

        Group root = new Group(); 
        root.getChildren().addAll(mainContainer);

        Scene scene = new Scene(root);
        scene.getStylesheets().add("/styles/Styles.css");

        stage.setTitle("Aplipikacka");
        stage.setScene(scene);
        stage.show();

        LoginDialog login = new LoginDialog();
        Optional <Pair<String, String>> result = login.getResult();
    }

    /**
     * The main() method is ignored in correctly deployed JavaFX application.
     * main() serves only as fallback in case the application can not be
     * launched through deployment artifacts, e.g., in IDEs with limited FX
     * support. NetBeans ignores main().
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}
LoginDialog.java

package cz.mono.monofx.fxml;

import java.util.Optional;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.util.Pair;

public class LoginDialog {

    private Dialog<Pair<String, String>> dialog;
    private TextField username;
    private PasswordField password;
    private Optional<Pair<String, String>> result;

    public LoginDialog() {
        dialog = new Dialog<>();
        dialog.setTitle("Login");
        dialog.setHeaderText("Provide correct login informations.");

        username = new TextField();
        username.setPrefSize(150, 30);
        username.setPromptText("username");
        username.setId("username");

        //Request focus on username by default
        Platform.runLater(()-> username.requestFocus());

        password = new PasswordField();
        password.setPrefSize(150, 30);
        password.setPromptText("password");
        password.setId("password");

        ButtonType loginButtonType = new ButtonType("Login", ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);

        Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
        loginButton.setId("loginButton");
        BooleanBinding bb = Bindings.createBooleanBinding(()-> username.getText().isEmpty() || password.getText().isEmpty(), username.textProperty(), password.textProperty());
        loginButton.disableProperty().bind(bb);

        GridPane grid = new GridPane();
        grid.setVgap(10);
        grid.setHgap(10);
        grid.setPadding(new Insets(20, 150, 10, 10));

        grid.add(new Label("Username: "), 0, 0);
        grid.add(username, 0, 1);
        grid.add(new Label("Password"), 1, 0);
        grid.add(password, 1, 1);

        dialog.getDialogPane().setContent(grid);

        // Convert the result to a username-password-pair when the login button is clicked.
        dialog.setResultConverter(dialogButton -> {
            if (dialogButton == loginButtonType) {
                return new Pair<>(username.getText(), password.getText());
            }
            return null;
        });

        result = dialog.showAndWait();
        result.ifPresent(usernamePassword -> {
            System.out.println("Username=" + usernamePassword.getKey() + ", Password=" + usernamePassword.getValue());
        });
        if (!result.isPresent()) {
            System.exit(0);
        };

    }

    /**
     * @return the result
     */
    public Optional<Pair<String, String>> getResult() {
        return result;
    }
}
包cz.mono.monofx.fxml;
导入java.util.Optional;
导入javafx.application.Platform;
导入javafx.beans.binding.Bindings;
导入javafx.beans.binding.BooleanBinding;
导入javafx.geometry.Insets;
导入javafx.scene.Node;
导入javafx.scene.control.ButtonBar.ButtonData;
导入javafx.scene.control.ButtonType;
导入javafx.scene.control.Dialog;
导入javafx.scene.control.Label;
导入javafx.scene.control.PasswordField;
导入javafx.scene.control.TextField;
导入javafx.scene.layout.GridPane;
导入javafx.util.Pair;
公共类登录{
私人对话;
私有文本字段用户名;
私有密码字段密码;
私人选择结果;
公共后勤{
dialog=新建dialog();
对话框.setTitle(“登录”);
setHeaderText(“提供正确的登录信息”);
用户名=新文本字段();
username.setPrefSize(150,30);
username.setPrompText(“用户名”);
username.setId(“用户名”);
//默认情况下,请求焦点在用户名上
Platform.runLater(()->username.requestFocus());
密码=新密码字段();
密码.setPrefSize(150,30);
密码。setPrompText(“密码”);
密码。setId(“密码”);
ButtonType loginButtonType=新的ButtonType(“登录”,ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType,ButtonType.CANCEL);
节点loginButton=dialog.getDialogPane().lookupButton(loginButtonType);
setId(“登录按钮”);
BooleanBinding bb=Bindings.createBooleanBinding(()->username.getText().isEmpty()| | | password.getText().isEmpty()、username.textProperty()、password.textProperty());
loginButton.disableProperty().bind(bb);
GridPane grid=新建GridPane();
网格设置间隙(10);
网格。setHgap(10);
网格设置填充(新插图(20、150、10、10));
添加(新标签(“用户名”)、0、0;
添加(用户名,0,1);
添加(新标签(“密码”),1,0);
添加(密码,1,1);
dialog.getDialogPane().setContent(网格);
//单击“登录”按钮时,将结果转换为用户名-密码对。
setResultConverter(dialogButton->{
如果(dialogButton==loginButtonType){
返回新的对(username.getText(),password.getText());
}
返回null;
});
结果=dialog.showAndWait();
结果.ifPresent(用户名密码->{
System.out.println(“Username=“+usernamePassword.getKey()+”,Password=“+usernamePassword.getValue());
});
如果(!result.isPresent()){
系统出口(0);
};
}
/**
*@返回结果
*/
公共可选getResult(){
返回结果;
}
}

让我展示一种可行的解决方案。我在我的项目中成功地使用了它

其主要思想是尽可能少地使用
start
应用程序方法:

public class MainApp extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(new MainPane(stage)));
        stage.show();
    }
所有逻辑都移动到
MainPane
class:

class MainPane extends BorderPane {
    MainPane(final Stage stage) {
        stage.setTitle("Aplipikacka");

        // open the login dialog only when the stage is opened too.
        stage.setOnShown(event -> Platform.runLater(this::showLoginDialog));
    }

    private void showLoginDialog() {
        LoginDialog login = new LoginDialog();
        Optional<Pair<String, String>> result = login.getResult();
        // TODO finish here
    }
}
最后,对测试进行一些改进:

public class MainAppFIT extends TestFxBase {
    @Test
    public void verifyLogin() {
        // given started application and opened login dialog
        sleep(500);

        // when
        write("HelloWorld");
        type(TAB);
        write("password");
        clickOn("#loginButton");

        // then
        // TODO finish here with verification of actual result
    }
}
因此,我能够看到textfx robot如何按测试步骤中的定义单击/键入

顺便说一句,`MainAppFIT'-FIT表示功能集成测试

class MainPane extends BorderPane {
    MainPane(final Stage stage) {
        stage.setTitle("Aplipikacka");

        // open the login dialog only when the stage is opened too.
        stage.setOnShown(event -> Platform.runLater(this::showLoginDialog));
    }

    private void showLoginDialog() {
        LoginDialog login = new LoginDialog();
        Optional<Pair<String, String>> result = login.getResult();
        // TODO finish here
    }
}
public class TestFxBase extends ApplicationTest {
    @Override
    public void start(Stage stage) {
        stage.setScene(new Scene(new MainPane(stage)));
        stage.show();
    }
}
public class MainAppFIT extends TestFxBase {
    @Test
    public void verifyLogin() {
        // given started application and opened login dialog
        sleep(500);

        // when
        write("HelloWorld");
        type(TAB);
        write("password");
        clickOn("#loginButton");

        // then
        // TODO finish here with verification of actual result
    }
}