Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TestFX October 2015

Avatar for hastebrot hastebrot
October 28, 2015

TestFX October 2015

Avatar for hastebrot

hastebrot

October 28, 2015
Tweet

Other Decks in Programming

Transcript

  1. 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(KeyCode.ENTER); // when: drag(".file").dropTo("#trash-can"); // then: verifyThat("#desktop", hasChildren(0, ".file")); } } Motivating Example
  2. 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(KeyCode.ENTER); // when: drag(".file").dropTo("#trash-can"); // then: verifyThat("#desktop", hasChildren(0, ".file")); } } Motivating Example
  3. Features • A fluent and clean API • Flexible setup

    and cleanup of JavaFX test fixtures • Simple robots to simulate user interactions • Rich collections of matchers to verify expected states • Support for ◦ Java 8 features and JavaFX 8 controls ◦ JUnit testing framework and Hamcrest matchers ◦ Precise screenshots of failed tests ◦ Headless testing using Monocle
  4. Features • A fluent and clean API • Flexible setup

    and cleanup of JavaFX test fixtures • Simple robots to simulate user interactions • Rich collections of matchers to verify expected states • Support for ◦ Java 8 features and JavaFX 8 controls ◦ JUnit testing framework and Hamcrest matchers ◦ Precise screenshots of failed tests ◦ Headless testing using Monocle
  5. Timeline • 2011, December: Java 7u2 includes JavaFX 2.0 in

    JDK • 2012, August: Java 7u6 includes JavaFX 2.2 in JRE and JDK • 2013, July: TestFX initial commit on GitHub • September: JavaZone Oslo • September: JavaOne 2013 San Francisco • December: TestFX 3.0.0 released • 2014, March: Java 8 includes JavaFX 8.0 • August: Java 8u20 includes Monocle. • 2015, February: TestFX 4.0.0-alpha released • September: JDK 9 Early Access with Project Jigsaw (Java Platform Module System)
  6. Timeline • 2011, December: Java 7u2 includes JavaFX 2.0 in

    JDK • 2012, August: Java 7u6 includes JavaFX 2.2 in JRE and JDK • 2013, July: TestFX initial commit on GitHub • September: JavaZone Oslo • September: JavaOne 2013 San Francisco • December: TestFX 3.0.0 released • 2014, March: Java 8 includes JavaFX 8.0 • August: Java 8u20 includes Monocle. • 2015, February: TestFX 4.0.0-alpha released • September: JDK 9 Early Access with Project Jigsaw (Java Platform Module System)
  7. Timeline • 2011, December: Java 7u2 includes JavaFX 2.0 in

    JDK • 2012, August: Java 7u6 includes JavaFX 2.2 in JRE and JDK • 2013, July: TestFX initial commit on GitHub • September: JavaZone Oslo • September: JavaOne 2013 San Francisco • December: TestFX 3.0.0 released • 2014, March: Java 8 includes JavaFX 8.0 • August: Java 8u20 includes Monocle. • 2015, February: TestFX 4.0.0-alpha released • September: JDK 9 Early Access with Project Jigsaw (Java Platform Module System)
  8. repositories { jcenter() // Bintray’s JCenter mavenCentral() // Sonatype’s Maven

    Central } // test source dependencies. dependencies { // testfx test library. testCompile(group: "org.testfx", name: "testfx-core", version: "4.0.+") testCompile(group: "org.testfx", name: "testfx-junit", version: "4.0.+") // junit test framework and hamcrest assertions. testCompile(group: "org.hamcrest", name: "hamcrest-all", version: "1.3") testCompile(group: "junit", name: "junit", version: "4.12") } Maven Artifacts (build.gradle)
  9. repositories { jcenter() // Bintray’s JCenter mavenCentral() // Sonatype’s Maven

    Central } // test source dependencies. dependencies { // testfx test library. testCompile(group: "org.testfx", name: "testfx-core", version: "4.0.+") testCompile(group: "org.testfx", name: "testfx-junit", version: "4.0.+") // junit test framework and hamcrest assertions. testCompile(group: "org.hamcrest", name: "hamcrest-all", version: "1.3") testCompile(group: "junit", name: "junit", version: "4.12") } Maven Artifacts (build.gradle)
  10. JavaFX Architecture • JavaFX: Java libraries designed to enable developers

    to create and deploy rich client applications. ◦ Stage, Scene, Node, Pane, Control, Shape, Event, Observable, FXRobot • Prism: High performance graphics engine. ◦ Software, OpenGL, DirectX ◦ Graphics, Color, Shape • Glass: Small and efficient windowing system. ◦ OS X, X11/Linux, Windows, iOS, Android ◦ Window, WindowEvent, InputDevice, Screen, Cursor, Clipboard, Robot ◦ Monocle is a rewrite of Lens, the Glass implementation for systems that do not have an underlying window manager.
  11. JavaFX Architecture • JavaFX: Java libraries designed to enable developers

    to create and deploy rich client applications. ◦ Stage, Scene, Node, Pane, Control, Shape, Event, Observable, FXRobot • Prism: High performance graphics engine. ◦ Software, OpenGL, DirectX ◦ Graphics, Color, Shape • Glass: Small and efficient windowing system. ◦ OS X, X11/Linux, Windows, iOS, Android ◦ Window, WindowEvent, InputDevice, Screen, Cursor, Clipboard, Robot ◦ Monocle is a rewrite of Lens, the Glass implementation for systems that do not have an underlying window manager.
  12. JavaFX Architecture • JavaFX: Java libraries designed to enable developers

    to create and deploy rich client applications. ◦ Stage, Scene, Node, Pane, Control, Shape, Event, Observable, FXRobot • Prism: High performance graphics engine. ◦ Software, OpenGL, DirectX ◦ Graphics, Color, Shape • Glass: Small and efficient windowing system. ◦ OS X, X11/Linux, Windows, iOS, Android ◦ Window, WindowEvent, InputDevice, Screen, Cursor, Clipboard, Robot ◦ Monocle is a rewrite of Lens, the Glass implementation for systems that do not have an underlying window manager.
  13. public class CounterApplication extends Application { public static void main(String[]

    args) { Application.launch(CounterApplication.class); } @Override public void start(Stage stage) { Scene scene = new Scene(new CounterPane(), 200, 50); stage.setScene(scene); stage.show(); } public static class CounterPane extends StackPane { public CounterPane() { // create countButton. Button countButton = new Button("count!"); countButton.setId("button"); // create countValue. TextField countValue = new TextField("0"); countValue.setId("counter");
  14. public class CounterApplication extends Application { public static void main(String[]

    args) { Application.launch(CounterApplication.class); } @Override public void start(Stage stage) { Scene scene = new Scene(new CounterPane(), 200, 50); stage.setScene(scene); stage.show(); } public static class CounterPane extends StackPane { public CounterPane() { // create countButton. Button countButton = new Button("count!"); countButton.setId("button"); // create countValue. TextField countValue = new TextField("0"); countValue.setId("counter");
  15. // initialize controls. countButton.setOnAction(event -> { int value = asInteger(countValue.getText());

    int incrementedValue = value + 1; countValue.setText(asString(incrementedValue)); }); countValue.setEditable(false); countValue.setPrefWidth(50); // create and add containers. HBox box = new HBox(10, countButton, countValue); box.setPadding(new Insets(10)); box.setAlignment(Pos.CENTER); getChildren().add(box); } private static int asInteger(String value) { return Integer.parseInt(value); } private static String asString(int value) { return value + ""; } } }
  16. public class Tech_01_Junit_Skeleton_V1 { @Test public void test_one() { System.out.println("test_one()");

    } @Test public void test_two() { System.out.println("test_two()"); } } Tests Passed: 2 passed > test_two() > test_one()
  17. public class Tech_01_Junit_Skeleton_V2 { @Before public void before() { System.out.println("before()");

    } @Test public void test_one() { System.out.println("test_one()"); } @Test public void test_two() { System.out.println("test_two()"); } } Tests Passed: 2 passed > before() > test_two() > before() > test_one()
  18. public class Tech_02_Setup_Stage_V1 { @Before public void before() { System.out.println("before()");

    Scene scene = new Scene(new CounterApplication.CounterPane(), 200, 50); Stage stage = new Stage(); stage.setScene(scene); stage.show(); } @Test public void test_one() { System.out.println("test_one()"); } @Test public void test_two() { System.out.println("test_two()"); } } Tests Failed: 0 passed, 2 failed > before() > java.lang.IllegalStateException: Toolkit not initialized > before() > java.lang.NoClassDefFoundError: Could not initialize class javafx.scene.control.Button
  19. public class Tech_02_Setup_Stage_V2 extends Application { @Before public void before()

    { System.out.println("before()"); new Thread(() -> Application.launch()).run(); } @Override public void start(Stage stage) { System.out.println("start()"); Scene scene = new Scene(new CounterApplication.CounterPane(), 200, 50); stage.setScene(scene); stage.show(); } @Test public void test_one() { System.out.println("test_one()"); } @Test public void test_two() { System.out.println("test_two()"); } } Tests Failed: 1 passed, 1 failed > before() > start() > test_two() > before() > java.lang.IllegalStateException: Application launch must not be called more than once
  20. public class Tech_02_Setup_Stage_V3 extends org.testfx.framework.junit.ApplicationTest { @Before public void before()

    { System.out.println("before()"); } @Override public void start(Stage stage) { System.out.println("start()"); Scene scene = new Scene(new CounterApplication.CounterPane(), 200, 50); stage.setScene(scene); stage.show(); } @Test public void test_one() { System.out.println("test_one()"); } @Test public void test_two() { System.out.println("test_two()"); } } Tests Passed: 2 passed > start() > before() > test_two() > start() > before() > test_one()
  21. public class Tech_02_Setup_Stage_V4 extends ApplicationTest { static { FxToolkit.toolkitContext().setSetupTimeoutInMillis(250); }

    @Override public void start(Stage stage) throws Exception { System.out.println("start()"); Thread.sleep(10000); } @Test public void test_one() { System.out.println("test_one()"); } @Test public void test_two() { System.out.println("test_two()"); } } Tests Failed: 0 passed, 4 failed > start() > java.util.concurrent.TimeoutException: Timeout waiting for task. > start() > java.util.concurrent.TimeoutException: Timeout waiting for task.
  22. public class Tech_03_Interact_and_Verify_V1 extends ApplicationTest { Scene scene; @Override public

    void start(Stage stage) { scene = new Scene(new CounterApplication.CounterPane(), 200, 50); stage.setScene(scene); stage.show(); } @Test public void test() { sleep(5, TimeUnit.SECONDS); } } Tests Passed: 1 passed
  23. public class Tech_03_Interact_and_Verify_V2 extends ApplicationTest { Scene scene; @Override public

    void start(Stage stage) {...} @Test public void test() { ((TextField) scene.lookup("#counter")).getText(); ((TextField) scene.lookup("#counter")).setText("3"); ((Button) scene.lookup("#button")).fire(); ((Button) scene.lookup("#button")).setText("click me!"); } } Tests Passed: 1 passed > java.lang.IllegalStateException: Not on FX application thread; currentThread = main
  24. public class Tech_03_Interact_and_Verify_V3 extends ApplicationTest { Scene scene; @Override public

    void start(Stage stage) {...} @Test public void test() throws Exception { Platform.runLater(() -> { ((TextField) scene.lookup("#counter")).getText(); ((TextField) scene.lookup("#counter")).setText("3"); ((Button) scene.lookup("#button")).fire(); ((Button) scene.lookup("#button")).setText("click me!"); }); } } Tests Passed: 1 passed
  25. public class Tech_03_Interact_and_Verify_V4 extends ApplicationTest { Scene scene; @Override public

    void start(Stage stage) {...} @Test public void test() throws Exception { // when: Platform.runLater(() -> { ((TextField) scene.lookup("#counter")).setText("3"); ((Button) scene.lookup("#button")).fire(); }); // then: assertEquals("4", ((TextField) scene.lookup("#counter")).getText()); } } Tests Failed: 0 passed, 1 failed > org.junit.ComparisonFailure: > Expected :4 > Actual :0
  26. public class Tech_03_Interact_and_Verify_V5 extends ApplicationTest { Scene scene; @Override public

    void start(Stage stage) {...} @Test public void test() throws Exception { // when: Platform.runLater(() -> { ((TextField) scene.lookup("#counter")).setText("3"); ((Button) scene.lookup("#button")).fire(); }); Semaphore eventQueueSemaphore = new Semaphore(0); Platform.runLater(() -> eventQueueSemaphore.release()); eventQueueSemaphore.acquire(); // then: assertEquals("4", ((TextField) scene.lookup("#counter")).getText()); } } Tests Passed: 1 passed
  27. public class Tech_03_Interact_and_Verify_V6 extends ApplicationTest { Scene scene; @Override public

    void start(Stage stage) {...} @Test public void test() { // when: interact(() -> { ((TextField) scene.lookup("#counter")).setText("3"); ((Button) scene.lookup("#button")).fire(); }); // then: assertEquals("4", ((TextField) scene.lookup("#counter")).getText()); } } Tests Passed: 1 passed
  28. public class Tech_03_Interact_and_Verify_V7 extends ApplicationTest { Scene scene; @Override public

    void start(Stage stage) {...} @Test public void test() { // when: interact(() -> { from(rootNode(scene)).lookup("#counter").<TextField>queryFirst().setText("3"); from(rootNode(scene)).lookup("#button").<Button>queryFirst().fire(); }); // then: assertEquals("4", from(rootNode(scene)).lookup("#counter").<TextField>queryFirst().getText() ); } } Tests Passed: 1 passed
  29. public class Tech_03_Interact_and_Verify_V8 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test() { // when: interact(() -> { fromAll().lookup("#counter").<TextField>queryFirst().setText("3"); fromAll().lookup("#button").<Button>queryFirst().fire(); }); // then: assertEquals("4", fromAll().lookup("#counter").<TextField>queryFirst().getText()); } } Tests Passed: 1 passed
  30. public class Tech_04_Assert_V1 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // JUnit: org.junit.Assert. assertEquals("0", fromAll().lookup("#counter").<TextField>queryFirst().getText()); assertEquals("0", lookup("#counter").<TextField>queryFirst().getText()); } } Tests Passed: 1 passed
  31. public class Tech_04_Assert_V2 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // JUnit: org.junit.Assert. assertEquals("0", fromAll().lookup("#counter").<TextField>queryFirst().getText()); assertEquals("0", lookup("#counter").<TextField>queryFirst().getText()); // Hamcrest: org.hamcrest.MatcherAssert. assertThat(lookup("#counter").<TextField>queryFirst().getText(), equalTo("0")); assertThat(lookup("#counter").<TextField>queryFirst(), NodeMatchers.hasText("0")); assertThat(lookup("#counter").queryFirst(), NodeMatchers.hasText("0")); } } Tests Passed: 1 passed
  32. public class Tech_04_Assert_V3 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // JUnit: org.junit.Assert. assertEquals("0", fromAll().lookup("#counter").<TextField>queryFirst().getText()); assertEquals("0", lookup("#counter").<TextField>queryFirst().getText()); // Hamcrest: org.hamcrest.MatcherAssert. assertThat(lookup("#counter").<TextField>queryFirst().getText(), equalTo("0")); assertThat(lookup("#counter").<TextField>queryFirst(), NodeMatchers.hasText("0")); assertThat(lookup("#counter").queryFirst(), NodeMatchers.hasText("0")); // TestFX: org.testfx.api.FxAssert. verifyThat(lookup("#counter"), NodeMatchers.hasText("0")); verifyThat("#counter", NodeMatchers.hasText("0")); verifyThat("#counter", hasText("0")); } } Tests Passed: 1 passed
  33. public class Tech_05_Robot_V1 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // expect: verifyThat("#counter", hasText("0")); } @Test public void test_count_up() { // when: interact(() -> { fromAll().lookup("#button").<Button>queryFirst().fire(); }); // then: verifyThat("#counter", hasText("1")); } } Tests Passed: 1 passed
  34. public class Tech_05_Robot_V1 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // expect: verifyThat("#counter", hasText("0")); } @Test public void test_count_up() { // when: interact(() -> { fromAll().lookup("#button").<Button>queryFirst().fire(); }); // then: verifyThat("#counter", hasText("1")); } } Tests Passed: 1 passed
  35. public class Tech_05_Robot_V2 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // expect: verifyThat("#counter", hasText("0")); } @Test public void test_count_up() { // when: clickOn(fromAll().lookup("#button").<Button>queryFirst()); // then: verifyThat("#counter", hasText("1")); } } Tests Passed: 1 passed
  36. public class Tech_05_Robot_V3 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // expect: verifyThat("#counter", hasText("0")); } @Test public void test_count_up_twice() { // when: clickOn("#button").clickOn("#button"); // then: verifyThat("#counter", hasText("2")); } } Tests Passed: 1 passed
  37. public class Tech_05_Robot_V4 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // expect: verifyThat("#counter", hasText("0")); } @Test public void test_count_up() { // when: Node node = lookup("#button").queryFirst(); clickOn(point(node)); // then: verifyThat("#counter", hasText("1")); } } Tests Passed: 1 passed
  38. public class Tech_05_Robot_V5 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // expect: verifyThat("#counter", hasText("0")); } @Test public void test_count_up() { // when: Node node = lookup("#button").queryFirst(); clickOn(point(node).atPosition(Pos.CENTER)); // then: verifyThat("#counter", hasText("1")); } } Tests Passed: 1 passed
  39. public class Tech_05_Robot_V6 extends ApplicationTest { @Override public void start(Stage

    stage) {...} @Test public void test_initial_state() { // expect: verifyThat("#counter", hasText("0")); } @Test public void test_count_up() { // when: pos(Pos.CENTER).clickOn(point("#button")); // then: verifyThat("#counter", hasText("1")); } } Tests Passed: 1 passed
  40. Fin