JavaFX + maven + TestFX + монокль не работают вместе

У меня есть небольшой проект для демонстрации JavaFX + TestFX под Maven. Я использую:

  1. Java (TM) SE Runtime Environment (сборка 1.8.0_40-b26)
  2. Apache Maven 3.2.5 (12a6b3acb947671f09b81f49094c53f426d8cea1; 2014-12-14T21: 29: 23 + 04: 00)

Полный источник: https://github.com/alevohin/testfx-maven-example

У меня есть 2 проблемы, которые я не могу решить:

1) Когда я запускаю AppFXTest из IDE (IDEA), тест завершается неудачно с

java.lang.ClassNotFoundException: com.sun.glass.ui.monocle.MonoclePlatformFactory
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at com.sun.glass.ui.PlatformFactory.getPlatformFactory(PlatformFactory.java:42)
at com.sun.glass.ui.Application.run(Application.java:146)
at com.sun.javafx.tk.quantum.QuantumToolkit.startup(QuantumToolkit.java:263)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:211)
at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:675)
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:695)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(LauncherImpl.java:182)
at com.sun.javafx.application.LauncherImpl$$Lambda$4/367732825.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Failed to load Glass factory class

Это работает, если я отключаю Monocle. Но я вижу реальное окно JavaFX и реальные кнопки нажатия курсора (режим "без головы")

2) Когда я пытаюсь выполнить тест (evecute mvn clean test) через maven, я получаю еще одну ошибку

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.18.1:test (default-test) on project testfx-maven-example: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.18.1:test failed: java.lang.NoClassDefFoundError: javafx/stage/Stage: javafx.stage.Stage -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.18.1:test (default-test) on project testfx-maven-example: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.18.1:test failed: java.lang.NoClassDefFoundError: javafx/stage/Stage
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:224)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
...
Caused by: java.lang.ClassNotFoundException: javafx.stage.Stage
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.apache.maven.surefire.booter.IsolatedClassLoader.loadClass(IsolatedClassLoader.java:97)
... 48 more

Мой pom.xml выглядит так

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.alevohin</groupId>
    <artifactId>testfx-maven-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <url>https://github.com/alevohin/javafx-maven-example</url>
    <dependencies>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-all</artifactId>
            <version>1.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.9.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testfx</groupId>
            <artifactId>testfx-core</artifactId>
            <version>4.0.1-alpha</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testfx</groupId>
            <artifactId>testfx-junit</artifactId>
            <version>4.0.1-alpha</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jfxtras</groupId>
            <artifactId>openjfx-monocle</artifactId>
            <version>1.8.0_20</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
                <configuration>
                    <forkCount>1</forkCount>
                    <reuseForks>false</reuseForks>
                    <threadCount>1</threadCount>
                    <systemPropertyVariables>
                        <glass.platform>Monocle</glass.platform>
                        <monocle.platform>Headless</monocle.platform>
                        <prism.order>sw</prism.order>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

AppFXTest

public final class AppFXTest extends ApplicationTest {

    @Override
    public void start(final Stage stage) throws Exception {
        new AppFX().start(stage);
    }

    @Test
    public void showsButtons() throws Exception {
        MatcherAssert.assertThat(
            lookup(".button").queryAll().size(),
            Matchers.is(2)
        );
    }

    @Test
    public void showsBottom() throws Exception {
        final Label label = lookup("BOTTOM").queryFirst();
        MatcherAssert.assertThat(
            label.isVisible(),
            Matchers.is(true)
        );
    }

    @Test
    public void clicksButtons() throws Exception {
        clickOn("A").clickOn("B");
    }
}

Приложение

public final class AppFX extends Application {

    /**
     * Program entry point.
     * @param args Program arguments.
     */
    public static void main(final String... args) {
        Application.launch(AppFX.class, args);
    }

    @Override
    public void start(final Stage stage) throws Exception {
        stage.setTitle("TEST");
        final Scene scene = new Scene(this.centralPane());
        scene.getStylesheets().add(
            AppFX.class.getResource("App.css").toExternalForm()
        );
        stage.setScene(scene);
        stage.setMinWidth(800.0D);
        stage.setMinHeight(600.0D);
        stage.show();
    }

    private Pane centralPane() {
        final BorderPane border = new BorderPane();
        border.setTop(this.buttonsPane());
        border.setCenter(this.centerPane());
        border.setBottom(this.bottomPane());
        return border;
    }

    private Pane buttonsPane() {
        final HBox controls = new HBox();
        controls.setSpacing(5.0D);
        controls.setAlignment(Pos.BASELINE_CENTER);
        controls.getChildren().add(new Label("BUTTONS"));
        controls.getChildren().add(new Button("A"));
        controls.getChildren().add(new Button("B"));
        return controls;
    }

    private Pane centerPane() {
        final VBox pane = new VBox();
        pane.setAlignment(Pos.CENTER);
        pane.getChildren().add(new Label("CENTER"));
        pane.setMinSize(600.0D, 400.0D);
        return pane;
    }

    private Pane bottomPane() {
        final VBox bottom = new VBox();
        final HBox status = new HBox();
        status.setAlignment(Pos.BASELINE_RIGHT);
        bottom.getChildren().add(status);
        status.getChildren().add(new Label("BOTTOM"));
        return bottom;
    }

}

ОБНОВЛЕНО

Проблема с

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.18.1:test (default-test) on project testfx-maven-example: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.18.1:test failed: java.lang.NoClassDefFoundError: javafx/stage/Stage: javafx.stage.Stage -> [Help 1]

решается добавлением в pom.xml additionalClasspathElements как это

 <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
                <configuration>
                    ...
                    <additionalClasspathElements>
                        <additionalClasspathElement>${java.home}/lib/ext/jfxrt.jar</additionalClasspathElement>
                    </additionalClasspathElements>
                </configuration>
            </plugin>

2 ответа

Failed to load Glass factory class

Раньше у меня тоже была эта ошибка, в моем случае я вручную собирал Monocle .jar и копировал его в JVM (jre/lib/ext).

Соберите Monocle в соответствии с документацией на https://github.com/TestFX/Monocle, либо соберите его из репозитория OpenJDK.

Так что у меня сработало следующее:

  • Скопируйте Monocle .jar, который вы строите, в jre / lib / ext вашего JDK.
  • Запустите ваше приложение со следующими аргументами JVM:
-Dtestfx.robot=glass
-Dglass.platform=Monocle
-Dmonocle.platform=Headless
-Dprism.order=sw

-Dtestfx.robot=glass необходимо, потому что TestFX в противном случае использует AWTRobot, который перемещает вашу мышь, используя очередь системных событий. Возможно, этого дополнения достаточно, чтобы ваш тест работал правильно.

Глядя на ваш pom.xmlЮнит приносит свою собственную версию hamcrest-core, так что вы должны исключить это, чтобы избежать проблем (если вы хотите импортировать несколько совпадений подколенных сухожилий). Я имел обыкновение иметь ошибки, используя оба.

Для дальнейшего исследования, ссылка на которую предоставлена ​​@ItachiUchiha, и пост на https://zentrieredich.wordpress.com/2014/12/23/javafx-testen-mit-monocle/ помог мне продвинуться по пути TestFX с использованием Monocle.

Чтобы избавиться от

java.lang.ClassNotFoundException: com.sun.glass.ui.monocle.MonoclePlatformFactory

вместо того, чтобы копировать Monocle .jar в JVM (jre/lib/ext)), мы можем добавить.jar в наш проект и загрузить класс MonoclePlatformFactory во время выполнения в classpath, как показано ниже:

static {
            try {
                File AGENT_JAR = new File("../lib/openjfx-monocle-1.8.0_20.jar");
                Method addUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                addUrl.setAccessible(true);
                addUrl.invoke(PlatformFactory.class.getClassLoader(), AGENT_JAR.toURI().toURL());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

Я бы порекомендовал использовать TestFX 3.x, потому что 4.x все еще находится в альфа-версии.

Очень хороший блог о том, как протестировать ваше приложение и работу TestFX, приведен в:

Объяснение внутренностей TestFX

Для использования монокля, вы можете следовать:

Безголовое тестирование пользовательского интерфейса с TestFX и JavaFX 8

Другие вопросы по тегам