【问题标题】:How to test TornadoFX function that creates a new view?如何测试创建新视图的 TornadoFX 功能?
【发布时间】:2019-06-03 01:03:00
【问题描述】:

我想测试一个在 TornadoFX 中创建新视图的函数。但是,当我调用该函数时,出现此错误。

java.lang.ExceptionInInitializerError
    at tornadofx.ControlsKt.button(Controls.kt:190)
    at tornadofx.ControlsKt.button$default(Controls.kt:190)
    at view.PeopleMenuView$setupTopBox$1$1.invoke(PeopleMenuView.kt:33)
    at view.PeopleMenuView$setupTopBox$1$1.invoke(PeopleMenuView.kt:8)
    at tornadofx.LayoutsKt.vbox(Layouts.kt:388)
    at tornadofx.LayoutsKt.vbox$default(Layouts.kt:103)
    at view.PeopleMenuView$setupTopBox$1.invoke(PeopleMenuView.kt:31)
    at view.PeopleMenuView$setupTopBox$1.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$1.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$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.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())
    }

当我从测试中调用该方法时,我得到了错误。我用谷歌搜索,但没有太多关于这个的发现。

我希望能够测试创建视图的方法。谢谢。

【问题讨论】:

    标签: unit-testing model-view-controller kotlin tornadofx


    【解决方案1】:

    一般来说:您不想测试 UI 视图。坚持住,我还是要告诉你怎么做。我只是想让您知道,这是一种罕见的情况,可能表明存在设计问题。

    如果没有 TestFx,这样的测试很难编写。使用 TestFx,它们更容易,但仍然非常缓慢且非常脆弱,并且您必须在任何标准 CI 环境中采取额外步骤才能允许它们在通常不具备运行 JavaFx 的构建盒上运行并且没有配备虚拟显示器。

    您(和 TestFx)遇到的最大问题是让您的线程正确进入测试。测试在一个线程上。 JavaFx 的可视化部分在另一个上。您自己的应用程序和 JavaFx 本身经常将它们的任务倒入 Platform.RunLater() 中,如果您不考虑该队列的清空,您将得到完全错误的结果,或者更糟糕的是,闪烁-y。睡眠在某些情况下有效,但 a) 睡眠会减慢您的速度,并且 b) 当您在较慢的机器上运行时效果不佳,例如云中配置较低的 windows 机器。

    回到您的一般问题:这可能意味着您的视图内连接很复杂,其中 UI 组件 X 依赖于 UI 组件 Y。广义地说,您希望 UI 组件 X 依赖于模型中的属性,并且您希望 UI组件 Y 依赖于模型中的属性,并且您希望模型处理属性之间的复杂交互。 TornadoFx 对此有直接支持,并且模型类不需要运行 UI 来进行测试。在某些情况下,控制器是放置互连逻辑的地方,但这种情况相对较少。我所做的大部分工作都不需要这样做,但确实需要 Model 类。 TornadoFx 的 ViewModel 和 ItemViewModel 类很好地支持 (Model != Domain) 的关键见解。

    话虽如此,如果您需要机器人控制视图,方法是使用 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,不管需要运行多少测试。

    我的实际经验:JavaFx 组件 属性关系“正常工作”。也就是说,我从测试中获得的收益很少,因为它不是我的代码,而且它“每次”都有效。 我的代码和每次都起作用的是属性之间的关系。这就是我使用 ViewModel 的原因,将属性之间的交互放在那里,并使用不需要运行 JavaFx 线程的微测试对它们进行严格测试。 (JavaFx 属性在其回调中不使用多线程,因此同步调用 addListener 目标。)当您需要巧妙地使用绑定时,这特别方便。在 TornadoFx 视图构建器中内联复杂的 JavaFx 绑定是一个难题。将它们提取到模型中非常容易。

    我通过反复试验学到了这一切。在此堆栈溢出中描述了第三种方法,它可以工作。这相当于使您的测试 100% 在 JavaFx 线程中运行。我发现这很困难,因为我的大部分工作都不是在那个线程上在生产中Basic JUnit test for JavaFX 8

    祝你好运,如果您需要更多帮助,请联系我们! GeePawHill

    附:向 Edvin 致敬:TornadoFx 的出色工作。我每天都用它。

    【讨论】:

      【解决方案2】:

      您需要初始化 JavaFX 工具包。如果您使用的是 TestFX,您可以调用 FxToolkit.registerPrimaryStage(),如果没有,您可以实例化一个 JFXPanel 以实现相同的目标。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-19
        • 2019-06-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-16
        • 1970-01-01
        • 1970-01-01
        • 2020-03-24
        相关资源
        最近更新 更多