java 如何使用 JUnit 对 JavaFX 控制器进行单元测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11385604/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you unit test a JavaFX controller with JUnit
提问by metasim
What's the proper way of initializing the JavaFX runtime so you can unit test (with JUnit) controllers that make use of the concurrency facilities and Platform.runLater(Runnable)
?
初始化 JavaFX 运行时的正确方法是什么,以便您可以单元测试(使用 JUnit)使用并发工具的控制器Platform.runLater(Runnable)
?
Calling Application.launch(...)
from the @BeforeClass
method results in a dead lock. If Application.launch(...)
is not called then the following error is thrown:
Application.launch(...)
从该@BeforeClass
方法调用会导致死锁。如果Application.launch(...)
未调用,则会引发以下错误:
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:121)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:116)
at javafx.application.Platform.runLater(Platform.java:52)
at javafx.concurrent.Task.runLater(Task.java:1042)
at javafx.concurrent.Task.updateMessage(Task.java:987)
at com.xyz.AudioSegmentExtractor.call(AudioSegmentExtractor.java:64)
at com.xyz.CompletionControllerTest.setUp(CompletionControllerTest.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access... // Inside test class
public static class AsNonApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// noop
}
}
@BeforeClass
public static void initJFX() {
Thread t = new Thread("JavaFX Init Thread") {
public void run() {
Application.launch(AsNonApp.class, new String[0]);
}
};
t.setDaemon(true);
t.start();
}
... // controller tests follow...
0(ParentRunner.java:42)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Followup: this is the motif I've been using based on recommendation by @SergeyGrinev.
跟进:这是我根据@SergeyGrinev 的推荐一直使用的主题。
public class MyTest extends ApplicationTest {
@Override
public void start (Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("mypage.fxml"));
stage.setScene(scene = new Scene(loader.load(), 300, 300));
stage.show();
}
采纳答案by Sergey Grinev
Calling launch()
from @BeforeClass
is a correct approach. Just note that launch()
doesn't return control to calling code. So you have to wrap it into new Thread(...).start()
.
调用launch()
from@BeforeClass
是一个正确的方法。请注意,launch()
这不会将控制权返回给调用代码。所以你必须把它包装成new Thread(...).start()
.
A 7 years later update:
7年后的更新:
Use TestFX! It will take care of launching in a proper way. E.g. you can extend your test from a TestFX's ApplicaionTest class and just use the same code:
使用TestFX!它将负责以适当的方式启动。例如,您可以从 TestFX 的 ApplicionTest 类扩展您的测试,并使用相同的代码:
@Test
public void testBlueHasOnlyOneEntry() {
clickOn("#tfSearch").write("blue");
verifyThat("#labelCount", hasText("1"));
}
and write tests like that:
并编写这样的测试:
@BeforeClass
public static void setUpClass() throws InterruptedException {
// Initialise Java FX
System.out.printf("About to launch FX App\n");
Thread t = new Thread("JavaFX Init Thread") {
public void run() {
Application.launch(AsNonApp.class, new String[0]);
}
};
t.setDaemon(true);
t.start();
System.out.printf("FX App thread started\n");
Thread.sleep(500);
}
回答by Michael Ellis
I found this to work,... but only after adding a Thread.sleep(500) after starting the JavaFX application thread. Presumably it takes some time to get the FX environment up and ready (about 200ms on my MacBook Pro retina)
我发现这可行,...但只有在启动 JavaFX 应用程序线程后添加 Thread.sleep(500) 之后。大概需要一些时间来启动和准备 FX 环境(在我的 MacBook Pro 视网膜上大约需要 200 毫秒)
##代码##