在Gradle测试中,仅显示失败测试的标准输出和错误信息

14

我有一个在travis-ci上运行的大型测试套件,其具有文本输出。 我希望以某种方式配置gradle,以便仅对于失败的测试,显示标准输出和标准错误流。 对于所有其他已正确执行的测试,不应发生这种情况,以免控制台被那些噪音污染。

我知道如何启用或禁用标准输出/错误日志记录,但我不确定如何根据测试结果使其发生。


与 [tag:travis-ci] 无关,这是纯粹的 [tag:gradle] 问题。 - набиячлэвэли
你使用哪个测试框架?你知道输出是使用日志框架编写的还是直接打印到stdout/stderr(System.out)中的吗? - TobiSH
我正在使用JUnit,作为Gradle测试任务的一部分被执行。 - Dr. Simon Harrer
3个回答

7
这可以通过以下Gradle配置实现。
project.test {
  def outputCache = new LinkedList<String>()

  beforeTest { TestDescriptor td -> outputCache.clear() }    // clear everything right before the test starts

  onOutput { TestDescriptor td, TestOutputEvent toe ->       // when output is coming put it in the cache
    outputCache.add(toe.getMessage())
    while (outputCache.size() > 1000) outputCache.remove() // if we have more than 1000 lines -> drop first
  }

  /** after test -> decide what to print */
  afterTest { TestDescriptor td, TestResult tr ->
    if (tr.resultType == TestResult.ResultType.FAILURE && outputCache.size() > 0) {
        println()
        println(" Output of ${td.className}.${td.name}:")
        outputCache.each { print(" > $it") }
    }
  }
}

Git仓库: https://github.com/calliduslynx/gradle-log-on-failure 原帖地址: https://discuss.gradle.org/t/show-stderr-for-failed-tests/8463/7

以上代码似乎并不能神奇地抑制输出,而只是在Gradle已经执行的基础上额外收集输出,并在之后打印出来。这就是为什么链接源中还要使用 events /*'started',*/ 'passed', 'failed' 的原因。只有这样才能真正减少输出,因为 standard_* 不是给定值的一部分。 - Thorsten Schöning

6

将以下配置块添加到您的build.gradle文件中:

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

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

文档可以在这里找到。


6
仅保留“TestLogEvent.FAILED”以仅显示失败的测试结果;如果没有将“showStandardStreams true”设置为真,则不会显示标准输出和错误输出,但这也适用于未失败的测试。 - Lorenzo Belli
这里是showStandardStreams相关文档 - JJD

1
自从Test.beforeTest和Test.onOutput方法需要一个Groovy Closure对象后,可以使用兼容性方法(closureOf、KotlinClosure2等),但我更喜欢用纯Kotlin编写自己的监听器。
import my.conventions.FailedTestLogger.Companion.logStdOutOnFailure

// ...

tasks {
    withType<Test>().configureEach {
        useJUnitPlatform()
        testLogging {
            showStackTraces = true
            exceptionFormat = TestExceptionFormat.FULL
            events = setOf(
                TestLogEvent.FAILED,
                TestLogEvent.SKIPPED,
            )
        }
        logStdOutOnFailure()
    }
}

package my.conventions

import org.gradle.api.logging.Logging
import org.gradle.api.tasks.testing.AbstractTestTask
import org.gradle.api.tasks.testing.TestDescriptor
import org.gradle.api.tasks.testing.TestListener
import org.gradle.api.tasks.testing.TestOutputEvent
import org.gradle.api.tasks.testing.TestOutputListener
import org.gradle.api.tasks.testing.TestResult
import java.util.concurrent.ConcurrentHashMap

/**
 * Only logs test output when a test fails.
 *
 * Example configuration
 *
 * ```kotlin
 * tasks.test {
 *     useJUnitPlatform()
 *     testLogging {
 *         showStackTraces = true
 *         exceptionFormat = TestExceptionFormat.FULL
 *         events = setOf(
 *             TestLogEvent.FAILED,
 *             TestLogEvent.SKIPPED,
 *         )
 *     }
 *     logStdOutOnFailure()
 * }
 * ```
 */
class FailedTestLogger : TestListener, TestOutputListener {
    private val testOutputs: MutableMap<TestDescriptor, MutableList<TestOutputEvent>> = ConcurrentHashMap()
    override fun beforeSuite(test: TestDescriptor) {
    }

    override fun afterSuite(testDescriptor: TestDescriptor, testResult: TestResult) {
        testOutputs.clear()
    }

    override fun beforeTest(testDescriptor: TestDescriptor) {}

    override fun afterTest(test: TestDescriptor, result: TestResult) {
        if (result.resultType === TestResult.ResultType.FAILURE) {
            LOGGER.error(buildString {
                append("## FAILURE: ")
                test.className?.let {
                    append(it)
                    append(".")
                }
                append(test.displayName)
            })
            for (output in testOutputs.getOrDefault(test, listOf())) {
                when (output.destination) {
                    TestOutputEvent.Destination.StdOut -> print(output.message)
                    TestOutputEvent.Destination.StdErr -> System.err.print(output.message)
                }

            }
            for (exception in result.exceptions) {
                exception.printStackTrace()
            }
        }
        testOutputs.remove(test)
    }

    override fun onOutput(test: TestDescriptor, outputEvent: TestOutputEvent) {
        testOutputs.compute(test) { _, value ->
            (value ?: mutableListOf()).also {
                it.add(outputEvent)
            }
        }
    }

    companion object {
        private val LOGGER = Logging.getLogger(FailedTestLogger::class.java)

        /**
         * Configure the test task to only logs test output when a test fails.
         *
         * Should be used at the end.
         */
        fun AbstractTestTask.logStdOutOnFailure() {
            if (!project.providers.systemProperty("idea.active").map { it.toBoolean() }.getOrElse(false)) {
                testLogging {
                    showStandardStreams = false
                }

                FailedTestLogger().also {
                    addTestListener(it)
                    addTestOutputListener(it)
                }
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接