eclipse-vertx / vertx-junit5

Testing Vert.x applications with JUnit 5
Apache License 2.0
44 stars 32 forks source link

Verticle/Web-Client Testing always times out #122

Closed Broeckelmann closed 1 year ago

Broeckelmann commented 2 years ago

Version

4.3.4

Context

I am using the vertx to run a web-server and now I want to test that said server. And I am using Kotlin instead of Java.

Problem Code

Test-code

import io.vertx.core.AbstractVerticle
import io.vertx.core.Vertx
import io.vertx.core.http.HttpServer
import io.vertx.ext.web.Router
import io.vertx.ext.web.client.WebClient
import io.vertx.ext.web.handler.StaticHandler
import io.vertx.junit5.VertxExtension
import io.vertx.junit5.VertxTestContext
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.io.File

@ExtendWith(VertxExtension::class)
class WelcomemeWebsiteVerticleTest {

    @BeforeEach
    fun `Deploy Website Verticle`(vertx: Vertx, testContext: VertxTestContext) {
        vertx.deployVerticle(WebsiteVerticle(), testContext.succeedingThenComplete())
    }

    @Test
    fun `Getting the Website root returns the Index html`(vertx: Vertx, testContext: VertxTestContext) {
                val port = 8080
        val indexFilePath = File("src/main/resources/webroot/index.html")
        val indexFileReader = indexFilePath.reader()
        val webClient = WebClient.create(vertx)
        testContext.assertComplete(webClient.get(port, "localhost", "/")
                                       .send())
            .onComplete { asyncHttpResponse ->
                assertThat(asyncHttpResponse.result()
                               .bodyAsString()).isEqualTo(indexFileReader.readText())
                indexFileReader.close()
                testContext.completeNow()
            }
    }

    @AfterEach
    fun `check that Verticle is still there`(vertx: Vertx, testContext: VertxTestContext) {
        assertThat(vertx.deploymentIDs()).isNotEmpty.hasSize(1)
    }
}

class WebsiteVerticle : AbstractVerticle() {

    override fun start() {
        val httpServer: HttpServer = vertx.createHttpServer()
                val port = 8080

        val staticHandler = StaticHandler.create("webroot")
        staticHandler.setCachingEnabled(false)
            .setFilesReadOnly(true)
        val router = Router.router(vertx)
        router.route("/*")
            .handler(staticHandler)
        router.route("/*")
            .handler { routingContext ->
                routingContext.response()
                    .putHeader("content-type", "text/html")
                    .sendFile("index.html")
            }

        httpServer.requestHandler(router)
            .listen(port) { httpServerStartupResult ->
                if (httpServerStartupResult.succeeded()) {
                    println("Successfully started website at localhost:$port/")
                }
                else {
                    println(httpServerStartupResult.cause())
                }
            }
    }
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1, user-scalable=yes">

    <title>Hello World!</title>
</head>
<body>
Hello World!

</body>
</html>

build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    val kotlinVersion = "1.7.20"

    kotlin("jvm") version kotlinVersion
    application
}

group = "com.test"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    val vertxVersion = "4.3.4"
    val assertjVersion = "3.23.1"

    testImplementation(kotlin("test"))
    testImplementation("io.vertx:vertx-junit5:${vertxVersion}")
    testImplementation("org.assertj:assertj-core:${assertjVersion}")
    testImplementation("io.vertx:vertx-web-client:${vertxVersion}")

    implementation("io.vertx:vertx-core:${vertxVersion}")
    implementation("io.vertx:vertx-lang-kotlin:${vertxVersion}")
    implementation("io.vertx:vertx-web:${vertxVersion}")
}

kotlin {
    val javaVersion = 17
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(javaVersion))
    }
    tasks.test {
        useJUnitPlatform()
    }
    tasks.withType<KotlinCompile> {
        kotlinOptions.jvmTarget = "$javaVersion"
    }
}

For some reason the testContext never gets completed and then the Test times out.

I used the testContext.assertComplete(...).onComplete(...) in all my other Vertx-Tests, but with the Web-Client it just does not work.

Any help would be appreciated 🙂.

tsegismont commented 1 year ago

Have you tried to use onComplete after sendJson to understand if the problem is with your web client call or vertx-junit5?

Broeckelmann commented 1 year ago

Yes I have, still times out sadly.

The assertion recieves the AsyncResult<HttpResponse> so that would mean that the web-client is done. I could add the response to the issue, but it's just a graphql response 🙂.

I have also tried closing the web-client manually after the assertion in case it is somehow blocking the testContext, but that does not changes anything.

tsegismont commented 1 year ago

Would you mind sharing a small reproducer?

Broeckelmann commented 1 year ago

I read up a bit on reproducers, sorry have never been asked that so far 😅.

I have changed the entire code snippets to a different verticle, but the problem remains the same. (to run it just build with gradle and then place the index.html file in src/main/resources/webroot/).

tsegismont commented 1 year ago

Found the problem, it's not a bug: you must complete the testContext in the method annotated with @AfterEach:

    @AfterEach
    fun `check that Verticle is still there`(vertx: Vertx, testContext: VertxTestContext) {
        assertThat(vertx.deploymentIDs()).isNotEmpty.hasSize(1)
        testContext.completeNow()
    }

Or simply not inject it:

    @AfterEach
    fun `check that Verticle is still there`(vertx: Vertx) {
        assertThat(vertx.deploymentIDs()).isNotEmpty.hasSize(1)
    }

In both cases, the test passes.

Broeckelmann commented 1 year ago

Oh wow, that does make sense.

Thank you very much, good sir😄👍🏻.