2017-08-02 55 views
3

我有一個簡單的javafx應用程序,其中,在stage.show()後,我打電話登錄對話框。當我運行測試時,他們不會開始工作,直到登錄對話框填充並確認爲manualy。出於測試目的,我試圖在單擊舞臺上的按鈕後顯示另一個對話框,並且通過testFx操作它沒有任何問題。唯一的問題是初始登錄對話框。有沒有辦法解決這種行爲,或者我做錯了什麼?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); 
    } 

} 

TestFxBase.java

package cz.mono.monofx.test; 

import cz.mono.monofx.MainApp; 
import java.util.concurrent.TimeoutException; 
import javafx.scene.Node; 
import javafx.scene.input.KeyCode; 
import javafx.scene.input.MouseButton; 
import javafx.stage.Stage; 
import org.junit.After; 
import org.junit.Before; 
import org.testfx.api.FxToolkit; 
import org.testfx.framework.junit.ApplicationTest; 

public class TestFxBase extends ApplicationTest { 

    @Before 
    public void setUpClass() throws Exception { 
     ApplicationTest.launch(MainApp.class); 
    } 

    @After 
    public void afterEachTest() throws TimeoutException { 
     FxToolkit.hideStage(); 
     release(new KeyCode[]{}); 
     release(new MouseButton[]{}); 
    } 

    //Helper method to retreve javafx components 
    public <T extends Node> T find(String query) { 
     return (T) lookup(query).queryAll().iterator().next(); 
    } 

    @Override 
    public void start(Stage stage) { 
     stage.show(); 
    } 
} 

SimpleTest.java

package cz.mono.monofx.test; 

import static javafx.scene.input.KeyCode.TAB; 
import org.junit.Test; 

public class ValidationTest extends TestFxBase { 

    @Test 
    public void verifyLogin() { 
     clickOn("#dialogButton"); 
     sleep(1000); 
     type(TAB); 
     sleep(1000); 
     type(TAB); 
     sleep(1000); 
    } 
} 

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; 
    } 
} 

回答

1

讓我來展示一個可能的工作解決方案。我成功地將它用於我的項目。

主要的想法是讓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 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 
    } 
} 

因爲textFx提供自己的stage,你應該手動注入你的場景做下一步:

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 
    } 
} 

至於結果我能怎麼看呢,如測試步驟定義textfx機器人點擊/類型。

順便說一下,`MainAppFIT' - FIT意味着功能集成測試。