简体   繁体   English

使用 Gradle Kotlin DSL 进行集成测试

[英]Integration tests with Gradle Kotlin DSL

I'm using this blog post to configure integration tests for a Spring Boot project, but I'm pretty stuck on declaring the source sets.我正在使用这篇博文为 Spring Boot 项目配置集成测试,但我一直坚持声明源集。 I also found this post on StackOverflow , but I think I'm a bit further already.我也在StackOverflow 上找到了这篇文章,但我想我已经更进一步了。

My project structure is我的项目结构是

project
|_ src
  |_ main
  | |_ kotlin
  | |_ resources
  |_ testIntegration
  | |_ kotlin
  | |_ resources
  |_ test
  | |_ kotlin
  | |_ resources
  |_ build.gradle.kts
  |_ ... other files

And build.gradle.ktsbuild.gradle.kts

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

plugins {
    idea
    kotlin("jvm")
    id("org.springframework.boot") version "2.0.5.RELEASE"
    id("org.jetbrains.kotlin.plugin.spring") version "1.2.71"
}

fun DependencyHandlerScope.springBoot(module: String) = this.compile("org.springframework.boot:spring-boot-$module:2.0.5.RELEASE")
fun DependencyHandlerScope.springBootStarter(module: String) = this.springBoot("starter-$module")

dependencies {
    springBoot("devtools")

    springBootStarter("batch")
    springBootStarter("... spring boot dependencies")


    compile("... more dependencies")

    testCompile("... more test dependencies")
}

val test by tasks.getting(Test::class) {
    useJUnitPlatform { }
}

kotlin {
    sourceSets {
        val integrationTest by creating {
            kotlin.srcDir("src/testIntegration/kotlin")
            resources.srcDir("src/testIntegration/resources")
        }
    }
}

val integrationTestCompile by configurations.creating {
    extendsFrom(configurations["testCompile"])
}
val integrationTestRuntime by configurations.creating {
    extendsFrom(configurations["testRuntime"])
}

val testIntegration by tasks.creating(Test::class) {
    group = "verification"
    testClassesDirs = kotlin.sourceSets["integrationTest"].kotlin
}

idea {
    module {
        testSourceDirs.addAll(kotlin.sourceSets["integrationTest"].kotlin.srcDirs)
        testSourceDirs.addAll(kotlin.sourceSets["integrationTest"].resources.srcDirs)
    }
}

I think I'm pretty much in the right direction.我认为我的方向几乎是正确的。 At least it doesn't throw an exception any more :)至少它不再抛出异常:)

When I run the testIntegration task, I get the following output:当我运行testIntegration任务时,我得到以下输出:

Testing started at 12:08 ...
12:08:49: Executing task 'testIntegration'...

> Task :project:compileKotlin UP-TO-DATE
> Task :project:compileJava NO-SOURCE
> Task :project:processResources UP-TO-DATE
> Task :project:classes UP-TO-DATE
> Task :project:compileTestKotlin UP-TO-DATE
> Task :project:compileTestJava NO-SOURCE
> Task :project:processTestResources UP-TO-DATE
> Task :project:testClasses UP-TO-DATE
> Task :project:testIntegration
BUILD SUCCESSFUL in 2s
5 actionable tasks: 1 executed, 4 up-to-date
12:08:51: Task execution finished 'testIntegration'.

Also, IntelliJ doesn't recognise the testIntegration directories as Kotlin packages.此外,IntelliJ 不会将 testIntegration 目录识别为 Kotlin 包。

I was finally able to figure it out thanks to some help on the Kotlin Slack channel.多亏了 Kotlin Slack 频道上的一些帮助,我终于能够弄清楚了。 First of all I had to upgrade to Gradle version 4.10.2.首先,我必须升级到 Gradle 4.10.2 版。

For more info have a look at these two pages from Gradle:有关更多信息,请查看 Gradle 中的这两页:

Then I just had to create the sourceSets for the integrationTests然后我只需要为 integrationTests 创建 sourceSets

sourceSets {
    create("integrationTest") {
            kotlin.srcDir("src/integrationTest/kotlin")
            resources.srcDir("src/integrationTest/resources")
            compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
            runtimeClasspath += output + compileClasspath + sourceSets["test"].runtimeClasspath
    }
}

This would work just fine for Java, but since I'm working with Kotlin I had to add an extra withConvention wrapper这适用于 Java,但由于我使用的是 Kotlin,因此我不得不添加一个额外的withConvention包装器

sourceSets {
    create("integrationTest") {
        withConvention(KotlinSourceSet::class) {
            kotlin.srcDir("src/integrationTest/kotlin")
            resources.srcDir("src/integrationTest/resources")
            compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
            runtimeClasspath += output + compileClasspath + sourceSets["test"].runtimeClasspath
        }
    }
}

In the docs they only put runtimeClasspath += output + compileClasspath , but I added sourceSets["test"].runtimeClasspath so I can directly use the test dependencies instead of declaring new dependencies for the integrationTest task.在文档中,他们只放置了runtimeClasspath += output + compileClasspath ,但我添加了sourceSets["test"].runtimeClasspath以便我可以直接使用测试依赖项而不是为integrationTest任务声明新的依赖项。

Once the sourceSets were created it was a matter of declaring a new task创建 sourceSets 后,就需要声明一个新任务

task<Test>("integrationTest") {
    description = "Runs the integration tests"
    group = "verification"
    testClassesDirs = sourceSets["integrationTest"].output.classesDirs
    classpath = sourceSets["integrationTest"].runtimeClasspath
    mustRunAfter(tasks["test"])
}

After this the tests still didn't run, but that was because I'm using JUnit4.在此之后,测试仍然没有运行,但那是因为我使用的是 JUnit4。 So I just had to add useJUnitPlatform() which makes this the final code所以我只需要添加useJUnitPlatform()这使它成为最终代码

task<Test>("integrationTest") {
    description = "Runs the integration tests"
    group = "verification"
    testClassesDirs = sourceSets["integrationTest"].output.classesDirs
    classpath = sourceSets["integrationTest"].runtimeClasspath
    mustRunAfter(tasks["test"])
    useJUnitPlatform()
}

I didnt like the use of withConvention and how the kotlin src dir was set.我不喜欢 withConvention 的使用以及如何设置 kotlin src 目录。 So after check out both gradle docs here and here , I came up with this:因此,在查看这里这里的两个 gradle 文档后,我想出了这个:

sourceSets {
    create("integrationTest") {
        kotlin {
            compileClasspath += main.get().output + configurations.testRuntimeClasspath
            runtimeClasspath += output + compileClasspath
        }
    }
}

val integrationTest = task<Test>("integrationTest") {
    description = "Runs the integration tests"
    group = "verification"
    testClassesDirs = sourceSets["integrationTest"].output.classesDirs
    classpath = sourceSets["integrationTest"].runtimeClasspath
    mustRunAfter(tasks["test"])
}

tasks.check {
    dependsOn(integrationTest)
}

I preferr the less verbose style when using kotlin { and the use of variable for the new integrationTestTask.我更kotlin {在使用kotlin {时使用不那么冗长的样式,并为新的 integrationTestTask 使用变量。

As of Gradle 5.2.1 see https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests从 Gradle 5.2.1 开始,请参阅https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests

sourceSets {
    create("intTest") {
        compileClasspath += sourceSets.main.get().output
        runtimeClasspath += sourceSets.main.get().output
    }
}

val intTestImplementation by configurations.getting {
    extendsFrom(configurations.testImplementation.get())
}

configurations["intTestRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get())

dependencies {
    intTestImplementation("junit:junit:4.12")
}

val integrationTest = task<Test>("integrationTest") {
    description = "Runs integration tests."
    group = "verification"

    testClassesDirs = sourceSets["intTest"].output.classesDirs
    classpath = sourceSets["intTest"].runtimeClasspath
    shouldRunAfter("test")
}

tasks.check { dependsOn(integrationTest) }

There are possible to implement integration testing in your project with itest gradle plugin . 使用itest gradle插件可以在您的项目中实施集成测试。

Just specify 只需指定

plugins {
  java
  id("com.softeq.gradle.itest") version "1.0.2"
}

tasks.withType<Test> {
  useJUnitPlatform()
}

in the gradle.build file and put your test code to the src/itest folder gradle.build文件中,并将测试代码放入src/itest文件夹

https://github.com/Softeq/itest-gradle-plugin https://github.com/Softeq/itest-gradle-plugin

https://softeq.github.io/itest-gradle-plugin/ https://softeq.github.io/itest-gradle-plugin/

- Ilya -伊利亚

Here is git repo that you can refer to: enter link description here这是您可以参考的 git repo: 在此处输入链接描述

import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
    application
    kotlin("jvm") version "1.3.72"
    id("com.diffplug.gradle.spotless") version "3.24.2"
    id("org.jmailen.kotlinter") version "1.26.0"
    checkstyle
}

version = "1.0.2"
group = "org.sample"

application {
    mainClass.set("org.sample.MainKt")
}

repositories {
    mavenCentral()
    jcenter()
}

tasks.checkstyleMain { group = "verification" }
tasks.checkstyleTest { group = "verification" }

spotless {
    kotlin {
        ktlint()
    }
    kotlinGradle {
        target(fileTree(projectDir).apply {
            include("*.gradle.kts")
        } + fileTree("src").apply {
            include("**/*.gradle.kts")
        })
        ktlint()
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
    testLogging {
        lifecycle {
            events = mutableSetOf(TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED)
            exceptionFormat = TestExceptionFormat.FULL
            showExceptions = true
            showCauses = true
            showStackTraces = true
            showStandardStreams = true
        }
        info.events = lifecycle.events
        info.exceptionFormat = lifecycle.exceptionFormat
    }

    val failedTests = mutableListOf<TestDescriptor>()
    val skippedTests = mutableListOf<TestDescriptor>()
    addTestListener(object : TestListener {
        override fun beforeSuite(suite: TestDescriptor) {}
        override fun beforeTest(testDescriptor: TestDescriptor) {}
        override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {
            when (result.resultType) {
                TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor)
                TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor)
                else -> Unit
            }
        }

        override fun afterSuite(suite: TestDescriptor, result: TestResult) {
            if (suite.parent == null) { // root suite
                logger.lifecycle("----")
                logger.lifecycle("Test result: ${result.resultType}")
                logger.lifecycle(
                        "Test summary: ${result.testCount} tests, " +
                                "${result.successfulTestCount} succeeded, " +
                                "${result.failedTestCount} failed, " +
                                "${result.skippedTestCount} skipped")
                failedTests.takeIf { it.isNotEmpty() }?.prefixedSummary("\tFailed Tests")
                skippedTests.takeIf { it.isNotEmpty() }?.prefixedSummary("\tSkipped Tests:")
            }
        }

        private infix fun List<TestDescriptor>.prefixedSummary(subject: String) {
            logger.lifecycle(subject)
            forEach { test -> logger.lifecycle("\t\t${test.displayName()}") }
        }

        private fun TestDescriptor.displayName() = parent?.let { "${it.name} - $name" } ?: "$name"
    })
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("com.sparkjava:spark-core:2.5.4")
    implementation("org.slf4j:slf4j-simple:1.7.30")

    testImplementation("com.squareup.okhttp:okhttp:2.5.0")
    testImplementation("io.kotest:kotest-runner-junit5-jvm:4.0.5")
    testImplementation("io.kotest:kotest-assertions-core-jvm:4.0.5") // for kotest core jvm assertions
    testImplementation("io.kotest:kotest-property-jvm:4.0.5")
}

sourceSets {
    create("integTest") {
        kotlin {
            compileClasspath += main.get().output + configurations.testRuntimeClasspath
            runtimeClasspath += output + compileClasspath
        }
    }
}

val integTest = task<Test>("integTest") {
    description = "Runs the integTest tests"
    group = "verification"
    testClassesDirs = sourceSets["integTest"].output.classesDirs
    classpath = sourceSets["integTest"].runtimeClasspath
    mustRunAfter(tasks["test"])
}

tasks.check {
    dependsOn(integTest)
}

sourceSets {
    create("journeyTest") {
        kotlin {
            compileClasspath += main.get().output + configurations.testRuntimeClasspath
            runtimeClasspath += output + compileClasspath
        }
    }
}

val journeyTest = task<Test>("journeyTest") {
    description = "Runs the JourneyTest tests"
    group = "verification"
    testClassesDirs = sourceSets["journeyTest"].output.classesDirs
    classpath = sourceSets["journeyTest"].runtimeClasspath
    mustRunAfter(tasks["integTest"])
}

tasks.check {
    dependsOn(journeyTest)
}

I hope this helps.我希望这有帮助。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM