如何测试创建新视图的 TornadoFX 函数?
How to test TornadoFX function that creates a new view?
我想测试在 TornadoFX 中创建新视图的功能。但是,当我调用该函数时,出现此错误。
java.lang.ExceptionInInitializerError
at tornadofx.ControlsKt.button(Controls.kt:190)
at tornadofx.ControlsKt.button$default(Controls.kt:190)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:33)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:8)
at tornadofx.LayoutsKt.vbox(Layouts.kt:388)
at tornadofx.LayoutsKt.vbox$default(Layouts.kt:103)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:31)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:8)
at tornadofx.LayoutsKt.hbox(Layouts.kt:384)
at tornadofx.LayoutsKt.hbox$default(Layouts.kt:96)
at view.PeopleMenuView.setupTopBox(PeopleMenuView.kt:29)
at view.PeopleMenuView.<init>(PeopleMenuView.kt:15)
at presenter.MainMenuPresenter.managePeoplePressed(MainMenuPresenter.kt:11)
at presenter.TestMainMenuPresenter.testManagePeoplePressed(TestMainMenuPresenter.kt:16)
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:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=10=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 36 more
这是因为在函数中创建了一个视图的新实例。简化的代码如下所示:
fun managePeoplePressed() {
view.replaceWith(PeopleMenuView())
}
当我从测试中调用该方法时,出现错误。我用谷歌搜索,但没有找到太多相关信息。
我希望能够测试创建视图的方法。谢谢。
您需要初始化 JavaFX 工具包。如果您使用的是 TestFX,您可以调用 FxToolkit.registerPrimaryStage()
,否则您可以实例化一个 JFXPanel
来实现相同的目标。
总的来说:您不想测试 UI 观看次数。等一下,我仍然会告诉你怎么做。我只想让您知道,这种情况很少见,表明存在设计问题。
如果没有 TestFx,这样的测试是很难编写的。使用 TestFx 它们更容易,但仍然非常慢且非常脆弱,并且您必须在任何标准 CI 环境中采取额外的步骤才能让它们 运行 在构建箱上,通常不配备 运行 JavaFx 并且不配备虚拟显示器。
您(和 TestFx)遇到的最大问题是让线程正确进入测试。测试在一个线程上。 JavaFx 的视觉部分在另一个上。您自己的应用程序和 JavaFx 本身经常将它们的任务倒入 Platform.RunLater(),如果您不考虑该队列的清空,您将得到的结果要么一律错误,要么更糟,闪烁.睡眠在某些情况下有效,但 a) 是睡眠并减慢你的速度,b) 当你 运行 在较慢的机器上时效果不佳,例如低配置的 windows 机器云.
回到您的一般问题:这可能意味着您的视图内连接很复杂,其中 UI 组件 X 依赖于 UI 组件 Y。大体上,您想要 UI组件 X 依赖于模型中的属性,并且您希望 UI 组件 Y 依赖于模型中的属性,并且您希望模型处理属性之间的复杂交互。 TornadoFx 对此有直接支持,模型 类 不需要 UI 运行 进行测试。在某些情况下,控制器是放置互连逻辑的地方,但这种情况相对较少。我所做的大部分工作并不需要它,但它确实需要模型 类。 (Model != Domain) 在 TornadoFx 的 ViewModel 和 ItemViewModel 类.
中得到了很好的支持
话虽如此,如果您需要对视图进行机器人控制,方法是使用 TestFx。如果你不这样做,这就是同意并阐述Edvin的回答,就是有这样的东西:
class UiTest {
companion object {
private var javaFxRunning: Boolean = false
fun start() {
StartWith.isUi = true
Errors.reallyShow = false
try {
runJavaFx()
} catch (e: InterruptedException) {
throw RuntimeException(e)
}
}
@Throws(InterruptedException::class)
private fun runJavaFx() {
if (javaFxRunning) return
val latch = CountDownLatch(1)
SwingUtilities.invokeLater {
JFXPanel()
latch.countDown()
}
latch.await()
javaFxRunning = true
}
}
}
在@BeforeEach 甚至在单独的@Tests 中,调用UiTest.start(),无论有多少测试需要它,它只会启动一次javafx 运行ning.
我的实际经验:JavaFx组件<->属性关系"just works"。也就是说,我从测试中得到的很少,因为它不是我的代码,但它可以工作 "every time"。 是我的代码和不每次都起作用的是属性之间的关系。这就是为什么我使用 ViewModel,将属性之间的交互放在那里,并使用微测试对其进行严格测试,这不需要 JavaFx 线程 运行ning。 (JavaFx 属性在其回调中不使用多线程,因此同步调用 addListener 目标。)当您需要巧妙地使用绑定时,这尤其方便。在 TornadoFx 视图生成器中内联复杂的 JavaFx 绑定是一个麻烦的游戏。将它们提取到模型中非常容易。
我通过反复试验学会了这一切。堆栈溢出中描述的第三种方法可以使用。这相当于在 JavaFx 线程中进行 100% 的测试 运行。我发现这很困难,因为我的大部分工作都不在那个线程上 在生产中 。 Basic JUnit test for JavaFX 8
祝您好运,如果您需要更多帮助,请联系我们!
GeePawHill
P.S。向 Edvin 大喊:TornadoFx 的出色工作。我每天都用它。
我想测试在 TornadoFX 中创建新视图的功能。但是,当我调用该函数时,出现此错误。
java.lang.ExceptionInInitializerError
at tornadofx.ControlsKt.button(Controls.kt:190)
at tornadofx.ControlsKt.button$default(Controls.kt:190)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:33)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:8)
at tornadofx.LayoutsKt.vbox(Layouts.kt:388)
at tornadofx.LayoutsKt.vbox$default(Layouts.kt:103)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:31)
at view.PeopleMenuView$setupTopBox.invoke(PeopleMenuView.kt:8)
at tornadofx.LayoutsKt.hbox(Layouts.kt:384)
at tornadofx.LayoutsKt.hbox$default(Layouts.kt:96)
at view.PeopleMenuView.setupTopBox(PeopleMenuView.kt:29)
at view.PeopleMenuView.<init>(PeopleMenuView.kt:15)
at presenter.MainMenuPresenter.managePeoplePressed(MainMenuPresenter.kt:11)
at presenter.TestMainMenuPresenter.testManagePeoplePressed(TestMainMenuPresenter.kt:16)
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:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=10=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 36 more
这是因为在函数中创建了一个视图的新实例。简化的代码如下所示:
fun managePeoplePressed() {
view.replaceWith(PeopleMenuView())
}
当我从测试中调用该方法时,出现错误。我用谷歌搜索,但没有找到太多相关信息。
我希望能够测试创建视图的方法。谢谢。
您需要初始化 JavaFX 工具包。如果您使用的是 TestFX,您可以调用 FxToolkit.registerPrimaryStage()
,否则您可以实例化一个 JFXPanel
来实现相同的目标。
总的来说:您不想测试 UI 观看次数。等一下,我仍然会告诉你怎么做。我只想让您知道,这种情况很少见,表明存在设计问题。
如果没有 TestFx,这样的测试是很难编写的。使用 TestFx 它们更容易,但仍然非常慢且非常脆弱,并且您必须在任何标准 CI 环境中采取额外的步骤才能让它们 运行 在构建箱上,通常不配备 运行 JavaFx 并且不配备虚拟显示器。
您(和 TestFx)遇到的最大问题是让线程正确进入测试。测试在一个线程上。 JavaFx 的视觉部分在另一个上。您自己的应用程序和 JavaFx 本身经常将它们的任务倒入 Platform.RunLater(),如果您不考虑该队列的清空,您将得到的结果要么一律错误,要么更糟,闪烁.睡眠在某些情况下有效,但 a) 是睡眠并减慢你的速度,b) 当你 运行 在较慢的机器上时效果不佳,例如低配置的 windows 机器云.
回到您的一般问题:这可能意味着您的视图内连接很复杂,其中 UI 组件 X 依赖于 UI 组件 Y。大体上,您想要 UI组件 X 依赖于模型中的属性,并且您希望 UI 组件 Y 依赖于模型中的属性,并且您希望模型处理属性之间的复杂交互。 TornadoFx 对此有直接支持,模型 类 不需要 UI 运行 进行测试。在某些情况下,控制器是放置互连逻辑的地方,但这种情况相对较少。我所做的大部分工作并不需要它,但它确实需要模型 类。 (Model != Domain) 在 TornadoFx 的 ViewModel 和 ItemViewModel 类.
中得到了很好的支持话虽如此,如果您需要对视图进行机器人控制,方法是使用 TestFx。如果你不这样做,这就是同意并阐述Edvin的回答,就是有这样的东西:
class UiTest {
companion object {
private var javaFxRunning: Boolean = false
fun start() {
StartWith.isUi = true
Errors.reallyShow = false
try {
runJavaFx()
} catch (e: InterruptedException) {
throw RuntimeException(e)
}
}
@Throws(InterruptedException::class)
private fun runJavaFx() {
if (javaFxRunning) return
val latch = CountDownLatch(1)
SwingUtilities.invokeLater {
JFXPanel()
latch.countDown()
}
latch.await()
javaFxRunning = true
}
}
}
在@BeforeEach 甚至在单独的@Tests 中,调用UiTest.start(),无论有多少测试需要它,它只会启动一次javafx 运行ning.
我的实际经验:JavaFx组件<->属性关系"just works"。也就是说,我从测试中得到的很少,因为它不是我的代码,但它可以工作 "every time"。 是我的代码和不每次都起作用的是属性之间的关系。这就是为什么我使用 ViewModel,将属性之间的交互放在那里,并使用微测试对其进行严格测试,这不需要 JavaFx 线程 运行ning。 (JavaFx 属性在其回调中不使用多线程,因此同步调用 addListener 目标。)当您需要巧妙地使用绑定时,这尤其方便。在 TornadoFx 视图生成器中内联复杂的 JavaFx 绑定是一个麻烦的游戏。将它们提取到模型中非常容易。
我通过反复试验学会了这一切。堆栈溢出中描述的第三种方法可以使用。这相当于在 JavaFx 线程中进行 100% 的测试 运行。我发现这很困难,因为我的大部分工作都不在那个线程上 在生产中 。 Basic JUnit test for JavaFX 8
祝您好运,如果您需要更多帮助,请联系我们! GeePawHill
P.S。向 Edvin 大喊:TornadoFx 的出色工作。我每天都用它。