JavaFX + maven + TestFX + монокль не работают вместе
У меня есть небольшой проект для демонстрации JavaFX + TestFX под Maven. Я использую:
- Java (TM) SE Runtime Environment (сборка 1.8.0_40-b26)
- 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