简体   繁体   English

Gradle:如何在控制台中实时显示测试结果?

[英]Gradle: How to Display Test Results in the Console in Real Time?

I would like to see test results ( system.out/err, log messages from components being tested ) as they run in the same console I run:我想查看测试结果(system.out/err,来自正在测试的组件的日志消息),因为它们在我运行的同一个控制台中运行:

gradle test

And not wait until tests are done to look at the test reports ( that are only generated when tests are completed, so I can't "tail -f" anything while tests are running )并且不要等到测试完成再查看测试报告(只有在测试完成时才会生成,所以在测试运行时我不能“tail -f”任何东西)

Here is my fancy version:这是我喜欢的版本:

花哨的测试结果

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

tasks.withType(Test) {
    testLogging {
        // set options for log level LIFECYCLE
        events TestLogEvent.FAILED,
               TestLogEvent.PASSED,
               TestLogEvent.SKIPPED,
               TestLogEvent.STANDARD_OUT
        exceptionFormat TestExceptionFormat.FULL
        showExceptions true
        showCauses true
        showStackTraces true

        // set options for log level DEBUG and INFO
        debug {
            events TestLogEvent.STARTED,
                   TestLogEvent.FAILED,
                   TestLogEvent.PASSED,
                   TestLogEvent.SKIPPED,
                   TestLogEvent.STANDARD_ERROR,
                   TestLogEvent.STANDARD_OUT
            exceptionFormat TestExceptionFormat.FULL
        }
        info.events = debug.events
        info.exceptionFormat = debug.exceptionFormat

        afterSuite { desc, result ->
            if (!desc.parent) { // will match the outermost suite
                def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
                def startItem = '|  ', endItem = '  |'
                def repeatLength = startItem.length() + output.length() + endItem.length()
                println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
            }
        }
    }
}

You could run Gradle with INFO logging level on the command line.您可以在命令行上使用 INFO 日志记录级别运行 Gradle。 It'll show you the result of each test while they are running.它会在运行时向您显示每个测试的结果。 Downside is that you will get far more output for other tasks also.缺点是你也会为其他任务获得更多的输出。

gradle test -i

You can add a Groovy closure inside your build.gradle file that does the logging for you:您可以在 build.gradle 文件中添加一个 Groovy 闭包来为您记录日志:

test {
    afterTest { desc, result -> 
        logger.quiet "Executing test ${desc.name} [${desc.className}] with result: ${result.resultType}"
    }
}

On your console it then reads like this:在你的控制台上,它是这样写的:

:compileJava UP-TO-DATE
:compileGroovy
:processResources
:classes
:jar
:assemble
:compileTestJava
:compileTestGroovy
:processTestResources
:testClasses
:test
Executing test maturesShouldBeCharged11DollarsForDefaultMovie [movietickets.MovieTicketsTests] with result: SUCCESS
Executing test studentsShouldBeCharged8DollarsForDefaultMovie [movietickets.MovieTicketsTests] with result: SUCCESS
Executing test seniorsShouldBeCharged6DollarsForDefaultMovie [movietickets.MovieTicketsTests] with result: SUCCESS
Executing test childrenShouldBeCharged5DollarsAnd50CentForDefaultMovie [movietickets.MovieTicketsTests] with result: SUCCESS
:check
:build

Since version 1.1 Gradle supports much more options to log test output .由于版本 1.1 Gradle 支持更多选项来记录测试输出 With those options at hand you can achieve a similar output with the following configuration:有了这些选项,您可以通过以下配置获得类似的输出:

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

Disclaimer: I am the developer of the Gradle Test Logger Plugin.免责声明:我是 Gradle 测试记录器插件的开发者。

You can simply use the Gradle Test Logger Plugin to print beautiful logs on the console.您可以简单地使用Gradle Test Logger Plugin在控制台上打印漂亮的日志。 The plugin uses sensible defaults to satisfy most users with little or no configuration but also offers a number of themes and configuration options to suit everyone.该插件使用合理的默认值来满足大多数用户很少或没有配置的需求,但也提供了许多主题和配置选项来适合每个人。

Examples例子

标准主题 Standard theme标准主题

摩卡主题 Mocha theme摩卡主题

Usage用法

plugins {
    id 'com.adarshr.test-logger' version '<version>'
}

Make sure you always get the latest version from Gradle Central .确保您始终从 Gradle Central获取最新版本

Configuration配置

You don't need any configuration at all.您根本不需要任何配置。 However, the plugin offers a few options.但是,该插件提供了一些选项。 This can be done as follows (default values shown):这可以按如下方式完成(显示默认值):

testlogger {
    // pick a theme - mocha, standard, plain, mocha-parallel, standard-parallel or plain-parallel
    theme 'standard'

    // set to false to disable detailed failure logs
    showExceptions true

    // set to false to hide stack traces
    showStackTraces true

    // set to true to remove any filtering applied to stack traces
    showFullStackTraces false

    // set to false to hide exception causes
    showCauses true

    // set threshold in milliseconds to highlight slow tests
    slowThreshold 2000

    // displays a breakdown of passes, failures and skips along with total duration
    showSummary true

    // set to true to see simple class names
    showSimpleNames false

    // set to false to hide passed tests
    showPassed true

    // set to false to hide skipped tests
    showSkipped true

    // set to false to hide failed tests
    showFailed true

    // enable to see standard out and error streams inline with the test results
    showStandardStreams false

    // set to false to hide passed standard out and error streams
    showPassedStandardStreams true

    // set to false to hide skipped standard out and error streams
    showSkippedStandardStreams true

    // set to false to hide failed standard out and error streams
    showFailedStandardStreams true
}

I hope you will enjoy using it.我希望你会喜欢使用它。

As stefanglase answered:正如stefanglase回答的那样:

adding the following code to your build.gradle (since version 1.1) works fine for output on passed , skipped and failed tests.将以下代码添加到您的build.gradle (自 1.1 版起)适用于通过跳过失败测试的输出。

test {
    testLogging {
        events "passed", "skipped", "failed", "standardOut", "standardError"
    }
}

What I want to say additionally (I found out this is a problem for starters) is that the gradle test command executes the test only one time per change .另外我想说的是(我发现这对初学者来说是一个问题)是gradle test命令每次更改只执行一次测试。

So if you are running it the second time there will be no output on test results .因此,如果您第二次运行它,则测试结果将没有输出 You can also see this in the building output: gradle then says UP-TO-DATE on tests.您还可以在构建输出中看到这一点:gradle 然后在测试中说UP-TO-DATE So its not executed a n-th time.所以它没有执行第 n 次。

Smart gradle!智能毕业!

If you want to force the test cases to run, use gradle cleanTest test .如果要强制运行测试用例,请使用gradle cleanTest test

This is slightly off topic but I hope it will help some newbies.这有点偏离主题,但我希望它会帮助一些新手。

edit编辑

As sparc_spread stated in the comments:正如sparc_spread在评论中所述:

If you want to force gradle to always run fresh tests (which might not always be a good idea) you can add outputs.upToDateWhen {false} to testLogging { [...] } .如果你想强制 gradle总是运行新的测试(这可能并不总是一个好主意),你可以添加testLogging { [...] } outputs.upToDateWhen {false}testLogging { [...] } Continue reading here .继续阅读这里

Peace.和平。

Add this to build.gradle to stop gradle from swallowing stdout and stderr.将此添加到build.gradle以阻止 gradle 吞下 stdout 和 stderr。

test {
    testLogging.showStandardStreams = true
}

It's documented here .它记录在这里

'test' task does not work for Android plugin, for Android plugin use the following: “测试”任务不适用于 Android 插件,对于 Android 插件,请使用以下内容:

// Test Logging
tasks.withType(Test) {
    testLogging {
        events "started", "passed", "skipped", "failed"
    }
}

See the following: https://stackoverflow.com/a/31665341/3521637请参阅以下内容: https : //stackoverflow.com/a/31665341/3521637

As a follow up to Shubham's great answer I like to suggest using enum values instead of strings .作为Shubham 出色答案的后续,我建议使用enum值而不是strings Please take a look at the documentation of the TestLogging class .请查看TestLogging 类文档

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

tasks.withType(Test) {
    testLogging {
        events TestLogEvent.FAILED,
               TestLogEvent.PASSED,
               TestLogEvent.SKIPPED,
               TestLogEvent.STANDARD_ERROR,
               TestLogEvent.STANDARD_OUT
        exceptionFormat TestExceptionFormat.FULL
        showCauses true
        showExceptions true
        showStackTraces true
    }
}

My favourite minimalistic version based on Shubham Chaudhary answer.我最喜欢的基于 Shubham Chaudhary 回答的简约版本。 在此处输入图片说明

Put this in build.gradle file:把它放在build.gradle文件中:

test {
    afterSuite { desc, result ->
    if (!desc.parent)
        println("${result.resultType} " +
            "(${result.testCount} tests, " +
            "${result.successfulTestCount} successes, " +
            "${result.failedTestCount} failures, " +
            "${result.skippedTestCount} skipped)")
    }
}

In Gradle using Android plugin:在 Gradle 中使用 Android 插件:

gradle.projectsEvaluated {
    tasks.withType(Test) { task ->
        task.afterTest { desc, result ->
            println "Executing test ${desc.name} [${desc.className}] with result: ${result.resultType}"
        }
    }
}

Then the output will be:然后输出将是:

Executing test testConversionMinutes [org.example.app.test.DurationTest] with result: SUCCESS执行测试 testConversionMinutes [org.example.app.test.DurationTest] 结果:成功

Just add the following closure to your build.gradle.只需将以下闭包添加到您的 build.gradle 中。 the output will be printed after the execution of every test.每次测试执行后都会打印输出。

test{
    useJUnitPlatform()
    afterTest { desc, result ->
        def output = "Class name: ${desc.className}, Test name: ${desc.name},  (Test status: ${result.resultType})"
        println( '\n' + output)
    }
}

Merge of Shubham's great answer and JJD use enum instead of string合并Shubham 的好答案JJD 使用枚举而不是字符串

tasks.withType(Test) {
   testLogging {
       // set options for log level LIFECYCLE
       events TestLogEvent.PASSED,
            TestLogEvent.SKIPPED, TestLogEvent.FAILED, TestLogEvent.STANDARD_OUT
       showExceptions true
       exceptionFormat TestExceptionFormat.FULL
       showCauses true
       showStackTraces true

    // set options for log level DEBUG and INFO
       debug {
        events TestLogEvent.STARTED, TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED, TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR
        exceptionFormat TestExceptionFormat.FULL
       }
       info.events = debug.events
       info.exceptionFormat = debug.exceptionFormat

       afterSuite { desc, result ->
           if (!desc.parent) { // will match the outermost suite
               def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
               def startItem = '|  ', endItem = '  |'
               def repeatLength = startItem.length() + output.length() + endItem.length()
               println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
           }
       }
   }
}

If you have a build.gradle.kts written in Kotlin DSL you can print test results with (I was developing a kotlin multi-platform project, with no "java" plugin applied):如果你有一个用Kotlin DSL编写的build.gradle.kts你可以打印测试结果(我正在开发一个 kotlin 多平台项目,没有应用“java”插件):

tasks.withType<AbstractTestTask> {
    afterSuite(KotlinClosure2({ desc: TestDescriptor, result: TestResult ->
        if (desc.parent == null) { // will match the outermost suite
            println("Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)")
        }
    }))
}

Following on from Benjamin Muschko's answer (19 March 2011), you can use the -i flag along with grep , to filter out 1000s of unwanted lines.Benjamin Muschko 的回答(2011 年 3 月 19 日)之后,您可以将-i标志与grep一起使用,以过滤掉数千条不需要的行。 Examples:例子:

Strong filter - Only display each unit test name and test result and the overall build status.强过滤器- 仅显示每个单元测试名称和测试结果以及整体构建状态。 Setup errors or exceptions are not displayed.不显示设置错误或异常。

./gradlew test -i | grep -E " > |BUILD"

Soft filter - Display each unit test name and test result, as well as setup errors/exceptions.软过滤器- 显示每个单元测试名称和测试结果,以及设置错误/异常。 But it will also include some irrelevant info:但它也会包含一些不相关的信息:

./gradlew test -i | grep -E -v "^Executing |^Creating |^Parsing |^Using |^Merging |^Download |^title=Compiling|^AAPT|^future=|^task=|:app:|V/InstrumentationResultParser:"

Soft filter, Alternative syntax: (search tokens are split into individual strings)软过滤器,替代语法:(搜索标记被拆分为单独的字符串)

./gradlew test -i | grep -v -e "^Executing " -e "^Creating " -e "^Parsing " -e "^Using " -e "^Merging " -e "^Download " -e "^title=Compiling" -e "^AAPT" -e "^future=" -e "^task=" -e ":app:" -e "V/InstrumentationResultParser:"

Explanation of how it works:工作原理说明:

The first command is ./gradlew test -i and "-i" means "Info/Verbose" mode, which prints the result of each test in real-time, but also displays large amounts of unwanted debug lines.第一个命令是./gradlew test -i"-i"表示“Info/Verbose”模式,它实时打印每个测试的结果,但也会显示大量不需要的调试行。

So the output of the first command, ./gradlew test -i , is piped to a second command grep , which will filter out many unwanted lines, based on a regular expression.因此,第一个命令./gradlew test -i的输出通过管道传输到第二个命令grep ,该命令将根据正则表达式过滤掉许多不需要的行。 "-E" enables the regular expression mode for a single string; "-E"为单个字符串启用正则表达式模式; "-e" enables regular expressions for multiple strings; "-e"为多个字符串启用正则表达式; and "|""|" in the regex string means "or".在正则表达式字符串中表示“或”。

In the strong filter, a unit test name and test result is allowed to display using " > " , and the overall status is allowed with "BUILD" .在强过滤器中,允许使用" > "显示单元测试名称和测试结果,使用"BUILD"允许显示整体状态。

In the soft filter, the "-v" flag means "not containing" and "^" means "start of line".在软过滤器中, "-v"标志表示“不包含”"^"表示“行首”。 So it strips out all lines that start with "Executing " or start with "Creating ", etc.所以它去掉了所有以“Executing”开头或以“Creating”开头的行,等等。


Example for Android instrumentation unit tests, with gradle 5.1:使用 gradle 5.1 的 Android 检测单元测试示例:

./gradlew connectedDebugAndroidTest --continue -i | grep -v -e \
    "^Transforming " -e "^Skipping " -e "^Cache " -e "^Performance " -e "^Creating " -e \
    "^Parsing " -e "^file " -e "ddms: " -e ":app:" -e "V/InstrumentationResultParser:"

Example for Jacoco unit test coverage, with gradle 4.10: Jacoco 单元测试覆盖率示例,使用 gradle 4.10:

./gradlew createDebugCoverageReport --continue -i | grep -E -v "^Executing |^Creating |^Parsing |^Using |^Merging |^Download |^title=Compiling|^AAPT|^future=|^task=|:app:|V/InstrumentationResultParser:"

For those using Kotlin DSL, you can do:对于那些使用 Kotlin DSL 的人,你可以这样做:

tasks {
  named<Test>("test") {
    testLogging.showStandardStreams = true
  }
}

If you are using jupiter and none of the answers work, consider verifying it is setup correctly:如果您使用的是 jupiter 并且所有答案都不起作用,请考虑验证它是否正确设置:

test {
    useJUnitPlatform()
    outputs.upToDateWhen { false }
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}

And then try the accepted answers然后尝试接受的答案

For Android, this works nicely:对于Android,这很好用:

android {
...

testOptions {
    unitTests.all {
        testLogging {
            outputs.upToDateWhen { false }
            events "passed", "failed", "skipped", "standardError"
            showCauses true
            showExceptions true
        }
    }
} 

} }

See Running Android unit / instrumentation tests from the console请参阅从控制台运行 Android 单元/仪器测试

A more comprehensive response for those using th Kotlin DSL:对使用 th Kotlin DSL 的人的更全面的回应:

subprojects {
    // all the other stuff
    // ...
    tasks.named<Test>("test") {
        useJUnitPlatform()
        setupTestLogging()
    }
}

fun Test.setupTestLogging() {
    testLogging {
        events(
            org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED,
            org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED,
            org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED,
            org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_OUT,
        )
        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
        showExceptions = true
        showCauses = true
        showStackTraces = true

        addTestListener(object : TestListener {
            override fun beforeSuite(suite: TestDescriptor) {}
            override fun beforeTest(testDescriptor: TestDescriptor) {}
            override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {}
            override fun afterSuite(suite: TestDescriptor, result: TestResult) {
                if (suite.parent != null) { // will match the outermost suite
                    val output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
                    val startItem = "|  "
                    val endItem = "  |"
                    val repeatLength = startItem.length + output.length + endItem.length
                    val messages = """
                        ${(1..repeatLength).joinToString("") { "-" }}
                        $startItem$output$endItem
                        ${(1..repeatLength).joinToString("") { "-" }}
                    """.trimIndent()
                    println(messages)
                }
            }
        })
    }
}

This should produce an output close to @odemolliens answers.这应该会产生接近@odemolliens 答案的输出。

I've written a test logger for Kotlin DSL.我为 Kotlin DSL 编写了一个测试记录器。 You can put this block on your project scope build.gradle.kts file.您可以将此块放在您的项目范围build.gradle.kts文件中。

subprojects {
    tasks.withType(Test::class.java) {
        testLogging {
            showCauses = false
            showExceptions = false
            showStackTraces = false
            showStandardStreams = false

            val ansiReset = "\u001B[0m"
            val ansiGreen = "\u001B[32m"
            val ansiRed = "\u001B[31m"
            val ansiYellow = "\u001B[33m"

            fun getColoredResultType(resultType: ResultType): String {
                return when (resultType) {
                    ResultType.SUCCESS -> "$ansiGreen $resultType $ansiReset"
                    ResultType.FAILURE -> "$ansiRed $resultType $ansiReset"
                    ResultType.SKIPPED -> "$ansiYellow $resultType $ansiReset"
                }
            }

            afterTest(
                KotlinClosure2({ desc: TestDescriptor, result: TestResult ->
                    println("${desc.className} | ${desc.displayName} = ${getColoredResultType(result.resultType)}")
                })
            )

            afterSuite(
                KotlinClosure2({ desc: TestDescriptor, result: TestResult ->
                    if (desc.parent == null) {
                        println("Result: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)")
                    }
                })
            )
        }
    }
}

在此处输入图片说明

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

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