Unit testing 如何测试创建新视图的功能?

Unit testing 如何测试创建新视图的功能?,unit-testing,model-view-controller,kotlin,tornadofx,Unit Testing,Model View Controller,Kotlin,Tornadofx,我想测试一个函数,其中在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

我想测试一个函数,其中在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
当我从测试中调用该方法时,我得到了错误。我在谷歌上搜索了一下,但没什么发现


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

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

您需要初始化JavaFXToolkit。如果您正在使用TestFX,您可以调用
FxToolkit.registerPrimaryStage()
,如果没有,您可以实例化
JFXPanel
,以实现相同的目标。

一般来说,您不想测试UI视图。抓紧点,我还是要告诉你怎么做。我只是想让你知道,这是一个罕见的情况,可以表明设计问题

没有TestFx,编写此类测试相当困难。有了TestFx,它们就更容易了,但仍然非常缓慢而且非常脆弱,您必须在任何标准CI环境中采取额外的步骤,以允许它们在构建框上运行,而构建框通常不具备运行JavaFx的能力,也不具备虚拟显示

您(和TestFx)遇到的最大问题是如何将线程放在测试中。测试在一个线程上进行。JavaFx的可视部分位于另一个页面上。您自己的应用程序和JavaFx本身经常将其任务放入Platform.RunLater(),如果您不考虑该队列的清空,您将得到一致错误的结果,或者更糟的是,结果是flick-y。在某些情况下,睡眠是有效的,但a)是睡眠,会让你变慢,b)当你在一个较慢的机器上运行时,比如在云中配置较低的windows机器上,睡眠就不能正常工作

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

话虽如此,如果您需要机器人控制视图,方法是使用TestFx。如果你不同意,这就是同意并详细阐述Edvin的答案,就是有这样的东西:

fun managePeoplePressed() {
        view.replaceWith(PeopleMenuView())
    }
   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
        }
    }
}

在@beforeach中,甚至在单个@Tests中,调用UiTest.start(),无论需要运行多少个测试,它都只会启动javafx一次

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

这一切我都是通过反复试验才学会的。还有第三种方法,如本堆栈溢出中所述,可以使其发挥作用。这相当于让100%的测试在JavaFx线程中运行。我发现这很难,因为我的大部分工作都不是在生产过程中进行的

祝你好运,如果你需要更多的帮助,请伸出援手! 吉普希尔


p.S.对埃德文喊道:龙卷风FX的伟大工作。我每天都使用它。

一般来说:您不想测试UI视图。抓紧点,我还是要告诉你怎么做。我只是想让你知道,这是一个罕见的情况,可以表明设计问题

没有TestFx,编写此类测试相当困难。有了TestFx,它们就更容易了,但仍然非常缓慢而且非常脆弱,您必须在任何标准CI环境中采取额外的步骤,以允许它们在构建框上运行,而构建框通常不具备运行JavaFx的能力,也不具备虚拟显示

您(和TestFx)遇到的最大问题是如何将线程放在测试中。测试在一个线程上进行。JavaFx的可视部分位于另一个页面上。您自己的应用程序和JavaFx本身经常将其任务放入Platform.RunLater(),如果您不考虑该队列的清空,您将得到一致错误的结果,或者更糟的是,结果是flick-y。在某些情况下,睡眠是有效的,但a)是睡眠,会让你变慢,b)当你在一个较慢的机器上运行时,比如在云中配置较低的windows机器上,睡眠就不能正常工作

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