簡體   English   中英

如何測試創建新視圖的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$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())
    }

當我從測試中調用該方法時,我得到了錯誤。 我到處搜尋,但是沒有太多可找的。

我希望能夠測試創建視圖的方法。 謝謝。

通常:您不想測試UI視圖。 緊緊,我仍然要告訴你如何。 我只想讓您知道這是一種罕見的情況,可以說明設計問題。

沒有TestFx,這樣的測試將很難編寫。 使用TestFx,它們更容易,但仍然非常慢且非常脆弱,並且您必須在任何標准CI環境中采取額外的步驟,以允許它們在通常不具備運行JavaFx功能的構建箱中運行並且沒有配備虛擬顯示器。

您(和TestFx)遇到的最大問題是將線程正確放入測試中。 測試是在一個線程上進行的。 JavaFx的可視部分在另一個上。 您自己的應用程序和JavaFx本身經常將其任務倒入Platform.RunLater()中,如果不計入該隊列的清空,您將得到錯誤統一的結果,或者更糟的是閃爍的結果。 睡眠在某些情況下有效,但是a)睡眠會使您減速,並且b)在較慢的機器(如雲中配置較低的Windows機器)上運行時,將無法正常工作。

回到您的一般問題:這可能意味着您的視圖內連接很復雜,其中UI組件X依賴於UI組件Y。廣義上,您希望UI組件X依賴於Model中的屬性,並且希望UI組件Y取決於模型中的屬性,並且您希望模型處理屬性之間的復雜交互。 TornadoFx為此提供了直接支持,並且Model類不需要測試運行的UI。 在某些情況下,控制器是放置互連邏輯的地方,但這是相對罕見的。 我所做的大部分工作都不需要這樣做,但是確實需要Model類。 (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。

我的實際經驗:JavaFx組件<->屬性關系“有效”。 也就是說,由於它不是我的代碼,因此我從測試中收獲很少,並且它“每次都有效”。 我的代碼什么, 並且每次都不起作用是屬性之間的關系。 這就是為什么我使用ViewModel,在其中放置屬性之間的交互,並使用microtests對其進行嚴格測試的原因,這不需要運行JavaFx線程。 (JavaFx屬性在其回調中不使用多線程,因此addListener目標被同步調用。)當您需要巧妙使用綁定時,這特別方便。 在TornadoFx視圖構建器中內聯復雜的JavaFx綁定是一個杯子的游戲。 將它們提取到模型很容易。

我通過反復試驗學到了這一切。 在堆棧溢出中有描述的第三種方法可以使之起作用。 這等於使您的測試的100%在JavaFx線程中運行。 我發現這很困難,因為我的大部分工作不在生產中的那個線程 JavaFX 8的基本JUnit測試

祝您好運,如果您需要更多幫助,請與我們聯系! GeePawHill

PS對埃德文大喊:TornadoFx的出色工作。 我每天都使用它。

您需要初始化JavaFX Toolkit。 如果使用的是TestFX,則可以調用FxToolkit.registerPrimaryStage() ,否則,可以實例化JFXPanel以實現相同的目標。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM