Search code examples
javajavafxtestfxjfoenix

How to test multiple scenes in TestFX


I wrote a unit test for ArithmeticProblem class in TestFX.

public class ArithmeticProblemTextFx extends TestFxBase {

    @Test(expected = FxRobotException.class)
    public void fxIdNotExist() {
        clickOn("#test123");
    }

    @Test
    public void allComponentsShouldHaveRightText() {
        verifyThat("#jfxButtonCheck", hasText("PRÜFEN"));
        verifyThat("#jfxButtonNextArithmeticProblem", hasText("NÄCHSTE AUFGABE"));
        verifyThat("#jfxButtonSave", hasText("SPEICHERN"));
        verifyThat("#jfxTextFieldResult", hasText(""));
    }

    @Test
    public void checkTextFieldResult() {
        JFXTextField jfxTextFieldResult = find("#jfxTextFieldResult");
        JFXButton jfxButtonCheck = find("#jfxButtonCheck");

        jfxTextFieldResult.setText("5");
        assertFalse(
                "Der Button ist darf NICHT disable sein, da eine Zahl in TextFieldResult steht.",
                jfxButtonCheck.isDisable()
        );

        jfxTextFieldResult.setText("g");
        assertTrue(
                "Der Button muss disable sein, da KEINE Zahl in TextFieldResult steht.",
                jfxButtonCheck.isDisable()
        );
    }

    @Test
    public void checkJfxButtonSaveClick() {

        clickOn("#jfxButtonSave");

        verifyThat("#labelTotalResults", hasText("0"));
        verifyThat("#labelNumberOfRightResults", hasText("0"));
        verifyThat("#labelAmountOfRightResults", hasText("(0%)"));
        verifyThat("#labelNumberOfWrongResults", hasText("0"));
        verifyThat("#labelAmountOfWrongResults", hasText("(0%)"));
        verifyThat("#labelTimePerArithmeticProblem", hasText(""));
        verifyThat("#labelMark", hasText("-"));
        verifyThat("#labelRightResult", hasText(""));
        verifyThat("#jfxTextFieldResult", hasText(""));
    }

    @Test
    public void checkJfxButtonNextArithmeticProblemClick() {

        clickOn("#jfxButtonNextArithmeticProblem");

        verifyThat("#labelRightResult", hasText(""));
        verifyThat("#jfxTextFieldResult", hasText(""));
        verifyThat("#labelTimePerArithmeticProblem", hasText(""));
    }
}

Here is the code of "TestFxBase"

public class TestFxBase extends ApplicationTest {

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

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

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

    public <T extends Node> T find(final String query) {
        return (T) lookup(query).queryAll().iterator().next();
    }
}

When I load the Main.class it only show the ArithmeticProblem-Scene. Here is how it looks.

enter image description here

So I can only test the ArithmeticProblem.class, because I do not know how I can load other scenes. In my menu I have got some other points like the evaluation or settings, how can I load this scenes?


Solution

  • Actually you should not test all your scenes in one test class. It violates SRP (Single Responsibility Principle). Instead you should separate your components, so that you could test them in isolation. i.e. ArithmeticProblem should be tested in ArithmeticProblemTest class and Settings class should be tested in SettingsTest class etc.

    There is a good example in TestFX github repository.

    public class DesktopPaneTest extends ApplicationTest {
        @Override
        public void start(Stage stage) {
            Scene scene = new Scene(new DesktopPane(), 800, 600);
            stage.setScene(scene);
            stage.show();
        }
    
        @Test
        public void should_drag_file_into_trashcan() {
            // given:
            rightClickOn("#desktop").moveTo("New").clickOn("Text Document");
            write("myTextfile.txt").push(ENTER);
    
            // when:
            drag(".file").dropTo("#trash-can");
    
            // then:
            verifyThat("#desktop", hasChildren(0, ".file"));
        }
    }
    

    in start method you may just add your component (JavaFX Node) to the scene and this example it is new DesktopPane(), in your case it will be new ArithmeticProblem(), new Settings() etc. And ApplicationTest.launch(Main.class); won't be needed any more.

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

    Notice that in this example only the behaviour is being tested by using Given-When-Then scenario. I recommend you to use this scenario in your tests.

    And these tests are also redundant I suppose:

    @Test(expected = FxRobotException.class)
    public void fxIdNotExist() {
        clickOn("#test123");
    }
    
    @Test
    public void allComponentsShouldHaveRightText() {
        verifyThat("#jfxButtonCheck", hasText("PRÜFEN"));
        verifyThat("#jfxButtonNextArithmeticProblem", hasText("NÄCHSTE AUFGABE"));
        verifyThat("#jfxButtonSave", hasText("SPEICHERN"));
        verifyThat("#jfxTextFieldResult", hasText(""));
    }
    

    Because the first test is just testing for non-existence, and the second one is just testing the text without doing anything. If you created textfields with those texts then they have to be there and they should be checked after some behaviour.

    I hope that my answer was useful for you. If you have some questions please feel free to write me.