项目作者: TestFX

项目描述 :
Simple and clean testing for JavaFX.
高级语言: Java
项目地址: git://github.com/TestFX/TestFX.git
创建时间: 2013-07-15T13:49:09Z
项目社区:https://github.com/TestFX/TestFX

开源协议:Other

下载


TestFX 4

TestFX 4 CI
Maven Central
Chat on Gitter

Simple and clean testing for JavaFX.

TestFX 4 requires Java version of 8 (1.8), or higher, and has only legacy support.

Documentation

  • See the Javadocs for latest master.
  • See the changelog CHANGES.md for latest released version.

Features

  • A fluent and clean API.
  • Flexible setup and cleanup of JavaFX test fixtures.
  • Simple robots to simulate user interactions.
  • Rich collection of matchers and assertions to verify expected states of JavaFX scene-graph nodes.

Support for:

Gradle

To add a dependency on TestFX using Gradle, use the following:

  1. dependencies {
  2. testCompile "org.testfx:testfx-core:4.0.18"
  3. }

Java 11+

Beginning with Java 11, JavaFX is no longer part of the JDK. It has been extracted to its own project: OpenJFX. This means, extra dependencies must be added to your project.

The easiest way to add the JavaFX libraries to your Gradle project is to use the JavaFX Gradle Plugin.

After following the README for the JavaFX Gradle Plugin you will end up with something like:

  1. plugins {
  2. id 'org.openjfx.javafxplugin' version '0.0.8'
  3. }
  4. javafx {
  5. version = '12'
  6. modules = [ 'javafx.controls', 'javafx.fxml' ]
  7. }

Test Framework

Next add a dependency corresponding to the testing framework you are using in your project. TestFX currently supports JUnit 4, JUnit 5, and Spock.

JUnit 4

  1. dependencies {
  2. testCompile "junit:junit:4.13-beta-3"
  3. testCompile "org.testfx:testfx-junit:4.0.18"
  4. }

JUnit 5

  1. dependencies {
  2. testCompile 'org.junit.jupiter:junit-jupiter-api:5.5.1'
  3. testCompile "org.testfx:testfx-junit5:4.0.18"
  4. }

Spock

  1. dependencies {
  2. testCompile "org.spockframework:spock-core:1.3-groovy-2.5"
  3. testCompile "org.testfx:testfx-spock:4.0.18"
  4. }

Matcher/Assertions Library

Finally you must add a dependency corresponding to the matcher/assertions libraries that you want to use with TestFX. TestFX currently supports Hamcrest matchers or AssertJ assertions.

Hamcrest

  1. testCompile group: 'org.hamcrest', name: 'hamcrest', version: '2.1'

AssertJ

  1. testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.2'

Maven

To add a dependency on TestFX using Maven, use the following:

  1. <dependency>
  2. <groupId>org.testfx</groupId>
  3. <artifactId>testfx-core</artifactId>
  4. <version>4.0.18</version>
  5. <scope>test</scope>
  6. </dependency>

Java 11+

Beginning with Java 11, JavaFX is no longer part of the JDK. It has been extracted to its own project: OpenJFX. This means, extra dependencies must be added to your project.

The easiest way to add the JavaFX libraries to your Maven project is to use the JavaFX Maven Plugin.

After following the README for the JavaFX Maven Plugin you will end up with something like:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.openjfx</groupId>
  4. <artifactId>javafx-controls</artifactId>
  5. <version>12.0.2</version>
  6. </dependency>
  7. </dependencies>
  8. <plugins>
  9. <plugin>
  10. <groupId>org.openjfx</groupId>
  11. <artifactId>javafx-maven-plugin</artifactId>
  12. <version>0.0.3</version>
  13. <configuration>
  14. <mainClass>hellofx/org.openjfx.App</mainClass>
  15. </configuration>
  16. </plugin>
  17. </plugins>

Have a look at Maven Central’s org.openjfx entry for an overview of available modules.

Test Framework

Next add a dependency corresponding to the testing framework you are using in your project. TestFX currently supports JUnit 4, JUnit 5, and Spock.

JUnit 4

  1. <dependency>
  2. <groupId>junit</groupId>
  3. <artifactId>junit</artifactId>
  4. <version>4.13-beta-3</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.testfx</groupId>
  9. <artifactId>testfx-junit</artifactId>
  10. <version>4.0.18</version>
  11. <scope>test</scope>
  12. </dependency>

JUnit 5

  1. <dependency>
  2. <groupId>org.junit.jupiter</groupId>
  3. <artifactId>junit-jupiter-api</artifactId>
  4. <version>5.5.1</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.testfx</groupId>
  9. <artifactId>testfx-junit5</artifactId>
  10. <version>4.0.18</version>
  11. <scope>test</scope>
  12. </dependency>

Spock

  1. <dependency>
  2. <groupId>org.spockframework</groupId>
  3. <artifactId>spock-core</artifactId>
  4. <version>1.3-groovy-2.5</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.testfx</groupId>
  9. <artifactId>testfx-spock</artifactId>
  10. <version>4.0.18</version>
  11. <scope>test</scope>
  12. </dependency>

Matcher/Assertions Library

Finally you must add a dependency corresponding to the matcher/assertions libraries that you want to use with TestFX. TestFX currently supports Hamcrest matchers or AssertJ assertions.

Hamcrest

  1. <dependency>
  2. <groupId>org.hamcrest</groupId>
  3. <artifactId>hamcrest</artifactId>
  4. <version>2.1</version>
  5. <scope>test</scope>
  6. </dependency>

AssertJ

  1. <dependency>
  2. <groupId>org.assertj</groupId>
  3. <artifactId>assertj-core</artifactId>
  4. <version>3.13.2</version>
  5. <scope>test</scope>
  6. </dependency>

Examples

Hamcrest Matchers

TestFX brings along a couple of custom Hamcrest matchers in package org.testfx.matcher.*.

AssertJ based Assertions

TestFX uses its own AssertJ based assertion implementation class: org.testfx.assertions.api.Assertions.

JUnit 4 with Hamcrest Matchers

  1. import org.junit.Test;
  2. import org.testfx.api.FxAssert;
  3. import org.testfx.framework.junit.ApplicationTest;
  4. import org.testfx.matcher.control.LabeledMatchers;
  5. import javafx.scene.Scene;
  6. import javafx.scene.control.Button;
  7. import javafx.scene.layout.StackPane;
  8. import javafx.stage.Stage;
  9. public class ClickableButtonTest_JUnit4Hamcrest extends ApplicationTest {
  10. private Button button;
  11. /**
  12. * Will be called with {@code @Before} semantics, i. e. before each test method.
  13. */
  14. @Override
  15. public void start(Stage stage) {
  16. button = new Button("click me!");
  17. button.setOnAction(actionEvent -> button.setText("clicked!"));
  18. stage.setScene(new Scene(new StackPane(button), 100, 100));
  19. stage.show();
  20. }
  21. @Test
  22. public void should_contain_button_with_text() {
  23. FxAssert.verifyThat(".button", LabeledMatchers.hasText("click me!"));
  24. }
  25. @Test
  26. public void when_button_is_clicked_text_changes() {
  27. // when:
  28. clickOn(".button");
  29. // then:
  30. FxAssert.verifyThat(".button", LabeledMatchers.hasText("clicked!"));
  31. }
  32. }

JUnit 4 with AssertJ based Assertions

  1. import org.junit.Test;
  2. import org.testfx.assertions.api.Assertions;
  3. import org.testfx.framework.junit.ApplicationTest;
  4. import javafx.scene.Scene;
  5. import javafx.scene.control.Button;
  6. import javafx.scene.layout.StackPane;
  7. import javafx.stage.Stage;
  8. public class ClickableButtonTest_JUnit4AssertJ extends ApplicationTest {
  9. private Button button;
  10. /**
  11. * Will be called with {@code @Before} semantics, i. e. before each test method.
  12. */
  13. @Override
  14. public void start(Stage stage) {
  15. button = new Button("click me!");
  16. button.setOnAction(actionEvent -> button.setText("clicked!"));
  17. stage.setScene(new Scene(new StackPane(button), 100, 100));
  18. stage.show();
  19. }
  20. @Test
  21. public void should_contain_button_with_text() {
  22. Assertions.assertThat(button).hasText("click me!");
  23. }
  24. @Test
  25. public void when_button_is_clicked_text_changes() {
  26. // when:
  27. clickOn(".button");
  28. // then:
  29. Assertions.assertThat(button).hasText("clicked!");
  30. }
  31. }

JUnit 5

TestFX uses JUnit5’s new extension mechanism via org.junit.jupiter.api.extension.ExtendWith. By using this, implementors are not forced anymore to inherit from ApplicationTest and are free to choose their own super classes.

It does also make use of JUnit5’s new dependency injection mechanism. By using this, test methods have access to the FxRobot instance that must be used in order to execute actions within the UI.

JUnit 5 with Hamcrest Matchers
  1. import org.junit.jupiter.api.Test;
  2. import org.junit.jupiter.api.extension.ExtendWith;
  3. import org.testfx.api.FxAssert;
  4. import org.testfx.api.FxRobot;
  5. import org.testfx.framework.junit5.ApplicationExtension;
  6. import org.testfx.framework.junit5.Start;
  7. import org.testfx.matcher.control.LabeledMatchers;
  8. import javafx.scene.Scene;
  9. import javafx.scene.control.Button;
  10. import javafx.scene.layout.StackPane;
  11. import javafx.stage.Stage;
  12. @ExtendWith(ApplicationExtension.class)
  13. class ClickableButtonTest_JUnit5Hamcrest {
  14. private Button button;
  15. /**
  16. * Will be called with {@code @Before} semantics, i. e. before each test method.
  17. *
  18. * @param stage - Will be injected by the test runner.
  19. */
  20. @Start
  21. private void start(Stage stage) {
  22. button = new Button("click me!");
  23. button.setId("myButton");
  24. button.setOnAction(actionEvent -> button.setText("clicked!"));
  25. stage.setScene(new Scene(new StackPane(button), 100, 100));
  26. stage.show();
  27. }
  28. /**
  29. * @param robot - Will be injected by the test runner.
  30. */
  31. @Test
  32. void should_contain_button_with_text(FxRobot robot) {
  33. FxAssert.verifyThat(button, LabeledMatchers.hasText("click me!"));
  34. // or (lookup by css id):
  35. FxAssert.verifyThat("#myButton", LabeledMatchers.hasText("click me!"));
  36. // or (lookup by css class):
  37. FxAssert.verifyThat(".button", LabeledMatchers.hasText("click me!"));
  38. }
  39. /**
  40. * @param robot - Will be injected by the test runner.
  41. */
  42. @Test
  43. void when_button_is_clicked_text_changes(FxRobot robot) {
  44. // when:
  45. robot.clickOn(".button");
  46. // then:
  47. FxAssert.verifyThat(button, LabeledMatchers.hasText("clicked!"));
  48. // or (lookup by css id):
  49. FxAssert.verifyThat("#myButton", LabeledMatchers.hasText("clicked!"));
  50. // or (lookup by css class):
  51. FxAssert.verifyThat(".button", LabeledMatchers.hasText("clicked!"));
  52. }
  53. }

JUnit 5 with AssertJ Assertions

  1. import org.junit.jupiter.api.Test;
  2. import org.junit.jupiter.api.extension.ExtendWith;
  3. import org.testfx.api.FxRobot;
  4. import org.testfx.assertions.api.Assertions;
  5. import org.testfx.framework.junit5.ApplicationExtension;
  6. import org.testfx.framework.junit5.Start;
  7. import javafx.scene.Scene;
  8. import javafx.scene.control.Button;
  9. import javafx.scene.layout.StackPane;
  10. import javafx.stage.Stage;
  11. @ExtendWith(ApplicationExtension.class)
  12. class ClickableButtonTest_JUnit5AssertJ {
  13. private Button button;
  14. /**
  15. * Will be called with {@code @Before} semantics, i. e. before each test method.
  16. *
  17. * @param stage - Will be injected by the test runner.
  18. */
  19. @Start
  20. private void start(Stage stage) {
  21. button = new Button("click me!");
  22. button.setId("myButton");
  23. button.setOnAction(actionEvent -> button.setText("clicked!"));
  24. stage.setScene(new Scene(new StackPane(button), 100, 100));
  25. stage.show();
  26. }
  27. /**
  28. * @param robot - Will be injected by the test runner.
  29. */
  30. @Test
  31. void should_contain_button_with_text(FxRobot robot) {
  32. Assertions.assertThat(button).hasText("click me!");
  33. // or (lookup by css id):
  34. Assertions.assertThat(robot.lookup("#myButton").queryAs(Button.class)).hasText("click me!");
  35. // or (lookup by css class):
  36. Assertions.assertThat(robot.lookup(".button").queryAs(Button.class)).hasText("click me!");
  37. // or (query specific type):
  38. Assertions.assertThat(robot.lookup(".button").queryButton()).hasText("click me!");
  39. }
  40. /**
  41. * @param robot - Will be injected by the test runner.
  42. */
  43. @Test
  44. void when_button_is_clicked_text_changes(FxRobot robot) {
  45. // when:
  46. robot.clickOn(".button");
  47. // then:
  48. Assertions.assertThat(button).hasText("clicked!");
  49. // or (lookup by css id):
  50. Assertions.assertThat(robot.lookup("#myButton").queryAs(Button.class)).hasText("clicked!");
  51. // or (lookup by css class):
  52. Assertions.assertThat(robot.lookup(".button").queryAs(Button.class)).hasText("clicked!");
  53. // or (query specific type)
  54. Assertions.assertThat(robot.lookup(".button").queryButton()).hasText("clicked!");
  55. }
  56. }

Spock with Hamcrest Matchers

  1. import org.testfx.framework.spock.ApplicationSpec;
  2. class ClickableButtonSpec extends ApplicationSpec {
  3. @Override
  4. void init() throws Exception {
  5. FxToolkit.registerStage { new Stage() }
  6. }
  7. @Override
  8. void start(Stage stage) {
  9. Button button = new Button('click me!')
  10. button.setOnAction { button.setText('clicked!') }
  11. stage.setScene(new Scene(new StackPane(button), 100, 100))
  12. stage.show()
  13. }
  14. @Override
  15. void stop() throws Exception {
  16. FxToolkit.hideStage()
  17. }
  18. def "should contain button"() {
  19. expect:
  20. verifyThat('.button', hasText('click me!'))
  21. }
  22. def "should click on button"() {
  23. when:
  24. clickOn(".button")
  25. then:
  26. verifyThat('.button', hasText('clicked!'))
  27. }
  28. }

Continuous Integration (CI)

Travis CI

To run TestFX tests as part of your Travis CI build on Ubuntu and/or macOS
take the following steps:

  1. Ensure that your unit tests are triggered as part of your build script. This
    is usually the default case when using Maven or Gradle.
  2. If you wish to test in a headless environment your must add Monocle
    as a test dependency:

    build.gradle

    1. dependencies {
    2. testCompile "org.testfx:openjfx-monocle:8u76-b04" // jdk-9+181 for Java 9, jdk-11+26 for Java 11
    3. }

    pom.xml

    1. <dependency>
    2. <groupId>org.testfx</groupId>
    3. <artifactId>openjfx-monocle</artifactId>
    4. <version>8u76-b04</version> <!-- jdk-9+181 for Java 9, jdk-11+26 for Java 11 -->
    5. <scope>test</scope>
    6. </dependency>
  3. Base your Travis configuration on the following. Some different build variations are shown (Glass/AWT robot,
    Headed/Headless, (Hi)DPI, etc.) adjust the build matrix to your requirements.

    .travis.yml

    1. language: java
    2. sudo: false # Linux OS: run in container
    3. matrix:
    4. include:
    5. # Ubuntu Linux (trusty) / Oracle JDK 8 / Headed (AWT Robot)
    6. - os: linux
    7. dist: trusty
    8. jdk: oraclejdk8
    9. env:
    10. - _JAVA_OPTIONS="-Dtestfx.robot=awt"
    11. # Ubuntu Linux (trusty) / Oracle JDK 8 / Headed (Glass Robot) / HiDPI
    12. - os: linux
    13. dist: trusty
    14. jdk: oraclejdk8
    15. env:
    16. - _JAVA_OPTIONS="-Dtestfx.robot=glass -Dglass.gtk.uiScale=2.0"
    17. # Ubuntu Linux (trusty) / Oracle JDK 8 / Headless
    18. - os: linux
    19. dist: trusty
    20. jdk: oraclejdk8
    21. env:
    22. - _JAVA_OPTIONS="-Djava.awt.headless=true -Dtestfx.robot=glass -Dtestfx.headless=true -Dprism.order=sw"
    23. # macOS / Oracle JDK 8 / Headless
    24. - os: osx
    25. osx_image: xcode9.4
    26. jdk: oraclejdk8
    27. env:
    28. - _JAVA_OPTIONS="-Djava.awt.headless=true -Dtestfx.robot=glass -Dtestfx.headless=true -Dprism.order=sw -Dprism.verbose=true"
    29. # Headed macOS is not currently possible on Travis.
    30. addons:
    31. apt:
    32. packages:
    33. - oracle-java8-installer
    34. before_install:
    35. - if [[ "${TRAVIS_OS_NAME}" == linux ]]; then export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; fi
    36. install: true
    37. before_script:
    38. - if [[ "${TRAVIS_OS_NAME}" == osx ]]; then brew update; brew cask reinstall caskroom/versions/java8; fi
    39. script:
    40. - ./gradlew check
    41. before_cache:
    42. - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
    43. - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
    44. - rm -f $HOME/.gradle/caches/*/fileHashes/fileHashes.bin
    45. - rm -f $HOME/.gradle/caches/*/fileHashes/fileHashes.lock
    46. cache:
    47. directories:
    48. - $HOME/.gradle/caches/
    49. - $HOME/.gradle/wrapper/
    50. - $HOME/.m2

Your TestFX tests should now run as part of your Travis CI build.

Appveyor (Windows)

To run TestFX tests as part of your Appveyor build on Windows take the following
steps:

  1. Ensure that your unit tests are triggered as part of your build script. This
    is usually the default case when using Maven or Gradle.
  2. If you wish to test in a headless environment your must add Monocle
    as a test dependency:

    build.gradle

    1. dependencies {
    2. testCompile "org.testfx:openjfx-monocle:8u76-b04" // jdk-9+181 for Java 9
    3. }

    pom.xml

    1. <dependency>
    2. <groupId>org.testfx</groupId>
    3. <artifactId>openjfx-monocle</artifactId>
    4. <version>8u76-b04</version> <!-- jdk-9+181 for Java 9 -->
    5. <scope>test</scope>
    6. </dependency>
  3. Base your Appveyor configuration on the following. Some different build variations are shown (Glass/AWT robot,
    Headed/Headless, (Hi)DPI, etc.) adjust the build matrix to your requirements.

    appveyor.yml

    1. version: "{branch} {build}"
    2. environment:
    3. matrix:
    4. # Java 8 / AWT Robot
    5. - JAVA_VERSION: "8"
    6. JAVA_HOME: C:\Program Files\Java\jdk1.8.0
    7. _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true"
    8. # Java 8 / AWT Robot / HiDPI
    9. - JAVA_VERSION: "8"
    10. JAVA_HOME: C:\Program Files\Java\jdk1.8.0
    11. _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true -Dglass.win.uiScale=200%"
    12. # Java 8 / Headless
    13. - JAVA_VERSION: "8"
    14. JAVA_HOME: C:\Program Files\Java\jdk1.8.0
    15. _JAVA_OPTIONS: "-Djava.awt.headless=true -Dtestfx.robot=glass -Dtestfx.headless=true -Dprism.order=sw -Dprism.text=t2k"
    16. # Java 10 / AWT Robot / HiDPI
    17. - JAVA_VERSION: "10"
    18. JAVA_HOME: C:\jdk10
    19. _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true -Dglass.win.uiScale=200%"
    20. # Java 11 / AWT Robot / HiDPI
    21. - JAVA_VERSION: "11"
    22. JAVA_HOME: C:\jdk11
    23. _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true -Dglass.win.uiScale=200%"
    24. build_script:
    25. - ps: |
    26. if ($env:JAVA_VERSION -eq "11") {
    27. $client = New-Object net.webclient
    28. $client.DownloadFile('http://jdk.java.net/11/', 'C:\Users\appveyor\openjdk11.html')
    29. $openJdk11 = cat C:\Users\appveyor\openjdk11.html | where { $_ -match "href.*https://download.java.net.*jdk11.*windows-x64.*zip\`"" } | %{ $_ -replace "^.*https:", "https:" } | %{ $_ -replace ".zip\`".*$", ".zip" }
    30. echo "Download boot JDK from: $openJdk11"
    31. $client.DownloadFile($openJdk11, 'C:\Users\appveyor\openjdk11.zip')
    32. Expand-Archive -Path 'C:\Users\appveyor\openjdk11.zip' -DestinationPath 'C:\Users\appveyor\openjdk11'
    33. Copy-Item -Path 'C:\Users\appveyor\openjdk11\*\' -Destination 'C:\jdk11' -Recurse -Force
    34. }
    35. elseif ($env:JAVA_VERSION -eq "10") {
    36. choco install jdk10 --version 10.0.2 --force --cache 'C:\ProgramData\chocolatey\cache' -params 'installdir=c:\\jdk10'
    37. }
    38. // Note: Currently Java 8 is the default JDK, if that changes the above will have to change accordingly.
    39. shallow_clone: true
    40. build:
    41. verbosity: detailed
    42. test_script:
    43. - gradlew build --no-daemon
    44. cache:
    45. - C:\Users\appveyor\.gradle\caches
    46. - C:\Users\appveyor\.gradle\wrapper -> .gradle-wrapper\gradle-wrapper.properties
    47. - C:\ProgramData\chocolatey\bin -> appveyor.yml
    48. - C:\ProgramData\chocolatey\lib -> appveyor.yml
    49. - C:\ProgramData\chocolatey\cache -> appveyor.yml

Chat

Head over to our gitter chat for discussion and questions.

TestFX Legacy: Deprecated

The testfx-legacy subproject is deprecated and no longer supported. It is highly recommended
that you switch from using testfx-legacy. If you want to continue using it you should cap
the versions of testfx-core and testfx-legacy to 4.0.8-alpha, which was the last released
version of testfx-legacy. Using a newer version of testfx-core with an older version of
testfx-legacy will very likely break (and does with testfx-core versions past 4.0.10-alpha).

Credits

Thanks to all of the contributors of TestFX!