为 Spek 使用 JUnit5 标签

Use JUnit5 Tags for Spek

我试图将我的测试区分为单元测试和集成测试。 我的想法是使用新的 JUnit5 Annotation @Tag("unit"),它非常适合我的 JUnit 测试,但我无法让它与 Spek 一起工作。

我目前拥有的是我的 class:

data class MyObject(val value: Int)

我的测试:

@Tag("unit")
object MyObjectTest {

    @Test
    fun checkEquality() {
        val o1 = MyObject(1)
        assertEquals(o1, o1)
    }
}

我的 build.gradle 有:

task utest(type: Test) {
    outputs.upToDateWhen { false }
    useJUnitPlatform {
        includeEngines 'junit-jupiter', 'junit-vintage', 'spek'
        includeTags 'unit'
        excludeTags 'performance', 'integration', 'functional'
    }


    testLogging {
        events "passed", "skipped", "failed"
    }
}

当我执行 utest 时,这有效。 然而,当用 Spek 做同样的事情时:

@Tag("unit")
object MyObjectSpek : Spek({

   given("an Object") {
       val o1 = MyObject(1)

       it("should be equal to itself") {
           assertEquals(o1, o1)
       }
   }
})

如果我 运行 gradle 任务测试它只执行 MyObjectTest 的方法而不执行 MyObjectSpek[=17= 的测试,会发生什么]

关于如何将 Spek 与 JUnit5 标签集成的任何想法或分离单元测试和集成测试的其他想法?

今天我 运行 遇到了完全相同的问题。我不得不将测试分为 3 个部分:单元、服务(测试 REST API)和集成(WebDriver)。

免责声明: 本指南适用于任何测试框架,而不仅仅是 SpekGradle 4.6 或更新版本需要 运行 这个。

将测试源集分成源集

在我的示例中,它们将是:

  • src/test — 用于单元测试(你已经有了)
  • src/serviceTest — 用于服务测试
  • src/integrationTest — 用于集成测试

所有这些集都应该有标准的源集结构。在您的项目中创建这些文件夹并将您的包移动到相应的源集。

完成后在 dependency section 之前添加到 build.gradle 以下行:

sourceSets {
    integrationTest {
        compileClasspath += main.output
        runtimeClasspath += main.output
    }
    serviceTest {
        compileClasspath += main.output
        runtimeClasspath += main.output
    }
}

configurations {
    integrationTestCompile.extendsFrom testCompile
    integrationTestRuntime.extendsFrom testRuntime

    serviceTestCompile.extendsFrom testCompile
    serviceTestRuntime.extendsFrom testRuntime
}

执行此操作后,您的 IDE(我想您使用的是 Idea)应该重新索引 build.gradle 并识别源集。您的新源集中可能有错误,因为它们看不到彼此的源。这是正确的,因为这些源集旨在 运行 独立,应该不是问题。

dependencies 分隔为适当的配置(可选)

默认情况下 serviceTestintegrationTest 继承所有 test 依赖项,但如果您需要将某些特定于特定配置的内容移出通用范围,您可以在此处执行此操作。

在我的例子中,WebDriver 非常重,除了集成测试,我不需要它。

dependencies {
    // available for all scopes
    testCompile "org.jetbrains.spek:spek-api:$spekVersion"
    testRuntime "org.jetbrains.spek:spek-junit-platform-engine:$spekVersion"
    testCompile "org.junit.platform:junit-platform-launcher:$junitPlatformVersion"

    // compiles only for integrationTest
    integrationTestCompile "org.seleniumhq.selenium:selenium-java:3.11.0"
    integrationTestCompile "org.seleniumhq.selenium.fluent:fluent-selenium:1.19"
}

设置执行顺序

我们需要添加 gradle 测试类型的任务并进行设置。不同的测试任务可以有不同的设置。

task serviceTest(type: Test) {
    // Runs tests from src/serviceTest
    testClassesDirs = sourceSets.serviceTest.output.classesDirs
    classpath = sourceSets.serviceTest.runtimeClasspath
}
// Setup serviceTest task 
serviceTest {
    // Uncomment this if you need to skip tests from the set after first failure. Since Gradle 4.6
    //failFast = true

    // Enable some logging
    testLogging {
        events "PASSED", "FAILED", "SKIPPED"
    }

    // Enable JUnit5 tests
    useJUnitPlatform {
    }
}

对集成测试执行相同的操作。

最后,设置依赖关系和执行顺序:

// Make service tests run during gradle check
check.dependsOn serviceTest
check.dependsOn integrationTest

// Make service tests run after unit tests
serviceTest.mustRunAfter test
// Make integration tests run after service tests
integrationTest.mustRunAfter serviceTest

结论

您将获得:

  1. Unit -> Service -> Integration 测试套件链 运行 严格按顺序排列;
  2. 如果您将在一个测试套件中获得测试失败(无论 failFast 选项如何),其余的将不会 运行 并浪费资源;
  3. 能够 运行 在 gradle <task> 的执行期间分别从控制台每个套件。

其他资源:

使用 IntelliJ 时需要考虑的另一件事是它对新源集有依赖性问题,请将其添加到您的 build.gradle:

apply plugin: 'idea'
idea {
    module {
        testSourceDirs += project.sourceSets.unitTest.kotlin.srcDirs
        testSourceDirs += project.sourceSets.unitTest.resources.srcDirs
        testSourceDirs += project.sourceSets.integrationTest.kotlin.srcDirs
        testSourceDirs += project.sourceSets.integrationTest.resources.srcDirs
        testSourceDirs += project.sourceSets.functionalTest.kotlin.srcDirs
        testSourceDirs += project.sourceSets.functionalTest.resources.srcDirs
    }
}