Testen von Kotlin / JS: Frameworks, Coroutinen und alles-alles-alles

Kotlin ist ein brillantes Projekt. Ursprünglich nur als JVM-Sprache konzipiert, erhielt es später Kompilierungsunterstützung für alle gängigen Plattformen, einschließlich JavaScript.





Einleitend. Ich habe ein Haustierprojekt - eine Website und eine API-Plattform für die Community für das Spiel Elite: Dangerous. Das Backend ist Kotlin / JVM (Ktor + Hibernate), das Frontend ist Kotlin / JS (KVision + Fomantic UI). Ich werde Ihnen später etwas über das Haustierprojekt erzählen und mehr auf der Vorderseite.





  • KVision ist ein Frontend-Framework für Kotlin, das Ideen aus verschiedenen Desktop-Frameworks (von Swing und JavaFX bis WinForms und Flutter) und Kotlin-Syntaxfunktionen wie DSL-Buildern kombiniert.





  • Fomantic-UI ist eine Abzweigung von Semantic-UI, einem HTML / JS-Webframework, das mit Bootstrap verglichen werden kann. Nur Fomantic ist interessanter.





Vor nicht allzu langer Zeit kam mir die Idee, diese beiden Welten zu verbinden und eine Bibliothek für KVision zu schreiben, was es zumindest einfacher machen würde, KVision-Seiten mit Fomantic-Elementen zu schreiben. Und wie es sich für ein Open-Source-Projekt gehört, wollte ich die Bibliothek mit Tests abdecken. Dieser Artikel handelt von diesem Abenteuer.





Der Code

Lassen Sie uns zunächst die Aufgabe definieren. Wir haben den folgenden Code für einen einfachen Fomantic-Button in unseren Händen:





package com.github.kam1sh.kvision.fomantic

import pl.treksoft.kvision.html.*
import pl.treksoft.kvision.panel.SimplePanel


open class FoButton(text: String) : Button(text = text, classes = setOf("ui", "button")) {
    var primary: Boolean = false
        set(value) {
            if (value) addCssClass("primary") else removeCssClass("primary")
            field = value
        }
}

fun SimplePanel.foButton(
    text: String,
    init: (FoButton.() -> Unit)? = null
): FoButton {
    val btn = FoButton(text)
    init?.invoke(btn)
    add(btn)
    return btn
}    

      
      



Und es gibt ein paar Tests:





package com.github.kam1sh.kvision.fomantic

import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlinx.browser.document
import kotlinx.coroutines.*
import pl.treksoft.kvision.panel.ContainerType
import pl.treksoft.kvision.panel.Root

class FoButtonTest {
    lateinit var kvapp: Root

    @BeforeTest
    fun before() {
        kvapp = Root("kvapp", containerType = ContainerType.NONE, addRow = false)        
    }

    @Test
    fun genericButtonTest() {
        kvapp.foButton("Button")
        assertEqualsHtml("""...""")
    }

    @Test
    fun buttonPrimaryTest() {
        val btn = kvapp.foButton("Button") { primary = true }
        assertEqualsHtml("""...""")
        btn.primary = false
        assertEqualsHtml("""...""")
    }
}

fun assertEqualsHtml(expected: String, message: String? = null) {
    val actual = document.getElementById("kvapp")?.innerHTML
    assertEquals(expected, actual, message)
}
      
      



: "" KVision div id=kvapp, HTML-.





. div? HTML- - document.body?.insertAdjacentHTML(...)



, , - ?





<source lang="html">
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.css">
    <script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.js"></script>
</head>
<body>
    <main>
        <div id="kvapp">

        </div>
    </main>
</body>
</html>
</source>
      
      



You've lost karma

Kotlin/JS.

For browser projects, it downloads and installs the Karma test runner with other required dependencies; for Node.js projects, the Mocha test framework is used.

. Karma Mocha. , Karma js- karma.config.d



.





Karma , -:





// karma.config.d/page.js
config.set({
  customContextFile: "../../../../../src/test/resources/test.html"
})
      
      



test.html, , src/test/resources/test.html



. - , Karma build/js/packages/kvision-fomantic-test/node_modules



, .





, ? ./gradlew browserTest



, ... Disconnected (0 times), because no message in 30000 ms.





, HTML- , JS-. build/js/node_modules/karma/static/context.html



.





main-:





<!-- The scripts need to be in the body DOM element, as some test running frameworks need the body
     to have already been created so they can insert their magic into it. For example, if loaded
     before body, Angular Scenario test framework fails to find the body and crashes and burns in
     an epic manner. -->
<script src="context.js"></script>
<script type="text/javascript">
    // Configure our Karma and set up bindings
    %CLIENT_CONFIG%
    window.__karma__.setupContext(window);

    // All served files with the latest timestamps
    %MAPPINGS%
</script>
<!-- Dynamically replaced with <script> tags -->
%SCRIPTS%
<!-- Since %SCRIPTS% might include modules, the `loaded()` call needs to be in a module too.
This ensures all the tests will have been declared before karma tries to run them. -->
<script type="module">
    window.__karma__.loaded();
</script>
<script nomodule>
    window.__karma__.loaded();
</script>
      
      



, ... , .





- . ? , HTTP- Ktor . Python async



, pytest pytest-async, .





suspend .





> Task :compileTestKotlinJs FAILED





e: ...src/test/kotlin/com/github/kam1sh/kvision/fomantic/FoButtonTest.kt: (44, 5): Unsupported [suspend test functions]





- Gradle





. . .





, runBlocking {}



. ...





runBlocking Kotlin/JVM.





, , , , , by design. GlobalScope.promise



, runBlocking :





fun runBlocking(block: suspend (scope: CoroutineScope) -> Unit) = GlobalScope.promise { block(this) }
      
      



. , . Karma :





config.set({
  client: {
    mocha: {
      timeout: 9000
    }
  }
})
      
      



. workaround. UPD: @ilgonmic, , 0. !





Mocha, , , , :





  • , done-, .





  • , Promise.





, , . , kotlin-test-js .

, , . , Promise, Mocha .





, , ? -- ?





- Kotest. .





. , .





// build.gradle.kts
testImplementation("io.kotest:kotest-assertions-core-js:4.3.2")
testImplementation("io.kotest:kotest-framework-api-js:4.3.2")
testImplementation("io.kotest:kotest-framework-engine:4.3.2")
      
      



class FoButtonTest : FunSpec({
    var kvapp: Root? = null
    beforeEach {
        kvapp = Root("kvapp", containerType = ContainerType.NONE, addRow = false)
    }
    test("generic button") {
        kvapp!!.foButton("Button")
        assertEqualsHtml("""...""")
    }

    test("primary button") {
        val btn = kvapp!!.foButton("Button") { primary = true }
        assertEqualsHtml("""...""")
        btn.primary = false
        delay(200)
        assertEqualsHtml("""...""")
    }
})
      
      



kotest , , FunSpec -- .





, , delay() suspend.





:





Das ist alles, was ich dir jetzt über Kotest erzählen kann. Ich werde die Bibliothek noch weiterentwickeln und wenn sie fertig ist, werde ich ihre Veröffentlichung in der Fomantic-Zwietracht und im Kotlin / kvision-Slack ankündigen. Und parallel dazu werde ich Kotest lernen.





Wenn Ihnen das nicht genug war, entschuldige ich mich: Ich wollte hier die Schlussfolgerung zeigen ./gradlew --debug browserTest



, aber die Vorbereitung dieses Materials hat sich aufgrund des Erscheinungsbilds des persönlichen Lebens bereits ziemlich verzögert. Wenn Sie also interessiert sind, sollten Sie die Gradle-Debug-Protokolle selbst in Betracht ziehen.





Na und? Naja nichts. Iss Kotlin / JS, trink Kotest und pass auf dich auf.








All Articles