当所有测试都是最新的时如何运行 Gradle 测试?

Posted

技术标签:

【中文标题】当所有测试都是最新的时如何运行 Gradle 测试?【英文标题】:How to run Gradle test when all tests are UP-TO-DATE? 【发布时间】:2015-06-08 05:43:30 【问题描述】:

我设置了成绩脚本。 当我执行 Gradle 构建时,一切正常,它运行 jUnit 测试。

之后,当我运行 Gradle 测试时,我得到以下信息:

C:\Users\..\..\Project>gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE

当我执行gradle clean 时,Gradle 构建当然可以工作...... 我希望能够只重置测试,而不是构建整个项目:我应该怎么做?

【问题讨论】:

根据给定的信息,这似乎是不必要的。如果应用程序代码和测试代码都没有改变,为什么需要重新运行测试? @Jolta 我的代码中的一些测试与 3 方输入相关,我运行测试不仅是为了确保我没有在代码中添加任何错误,还检查是否我得到的 3 方输入发生了一些变化 对不起,我很挑剔,但我真的不认为这是思考这个问题的正确方法:如果你有可变的 3 方输入不是处理这个问题的正确方法以某种方式模拟这些输入?测试实际上应该是关于测试你正在编写的代码。如果您依赖 3 方输入是不可接受的,那么您是否会面临相当明显的误报危险?该策略不应该将问题输入作为应用代码的一部分吗? @mikerodent 考虑针对第 3 方在线服务测试您的代码。您可能希望监控服务 API 中可能发生的变化,以便能够尽快对已部署的修复做出响应。 CI 测试不是这样做的好方法吗?使用模拟只会告诉您自己的代码没有回归,但依赖项仍然可能会发生变化。使用真实的服务,说明你的产品在当前环境下确实可以执行预期的操作。 从集成测试的角度来看,这也是有效的,其中测试的目的是验证您的代码与其他代码位的集成,在这种情况下不适合模拟依赖项 【参考方案1】:

一种选择是在Forcing tasks to execute 部分中使用--rerun-tasks 标志。这将重新运行所有测试任务及其依赖的所有任务。

如果您只对重新运行测试感兴趣,那么另一种选择是在执行测试之前让 gradle 清理测试结果。这可以使用cleanTest 任务来完成。

一些背景知识 - Java 插件为每个其他任务定义了一个干净的任务。根据Tasks 文档:

cleanTaskName - 删除指定任务创建的文件。 cleanJar 会删除 jar 任务创建的 JAR 文件,cleanTest 会删除 test 任务创建的测试结果。

因此,为了重新运行测试,您只需要运行cleanTest 任务,即:gradle cleanTest test

【讨论】:

gradle cleanTest test 不会重新运行测试,它会清理它们的输出,但test 任务仍会从缓存中获取测试结果 - 请参阅github.com/gradle/gradle/issues/9153 上面的评论是对的。但是如果你使用--no-build-cache,那么它将按预期工作,例如gradle cleanTest test --no-build-cache. --rerun-tasks 只是门票。 --no-build-cache 即使清洁对我来说也不起作用。谢谢@Amnon gradle cleanTest test 为我工作。【参考方案2】:

其他选项是在 build.gradle 中添加以下内容:

test.outputs.upToDateWhen false

【讨论】:

我将这种技术用于我创建的用于运行功能测试的funcTest 任务。 这是一种比接受的答案更好的方法,因为它只会应用于所需的任务。 upToDateWhen 可以以任何“代码驱动”的方式使用,例如系统属性、环境变量、项目属性等。 正如答案***.com/a/52484259/340175 提到的,有一篇有用的博客文章blog.gradle.org/stop-rerunning-tests 解释了为什么不建议将此方法作为一般方法。不过,我同意它可能有用并且可以满足问题的要求。 是的,这是过时的答案,当我写这个 Gradle 时,它​​是 2.11 版本,刚刚开始可用,但仍然有很多粗糙的边缘,今天已经打磨了。 很好的答案!!!使用参数传递它:gradle test -Prerun-tests。 build.gradle 中的代码:if(project.hasProperty("rerun-tests")) test.outputs.upToDateWhen false 【参考方案3】:

gradle test --rerun-tasks

指定忽略任何任务优化。

来源:https://gradle.org/docs/current/userguide/gradle_command_line.html

【讨论】:

我认为它也会重新运行所有相关任务,这不是询问者的预期行为。【参考方案4】:

这是最近 Gradle 的博文 Stop rerunning your tests 上的主题。 author 显示了一个使用 outputs.upToDateWhen false 的示例并解释了错误的原因:

这实际上并不强制重新运行

这个 sn-p 的作者可能想说的是“总是重新运行我的测试”。这不是这个 sn-p 所做的。它只会将任务标记为过期,强制 Gradle 重新创建输出。但事情是这样的,如果启用了构建缓存,Gradle 不需要运行任务来重新创建输出。它会在缓存中找到一个条目并将结果解压到测试的输出目录中。

这个sn-p也是如此:

test.dependsOn cleanTest

Gradle 将在清理输出后从构建缓存中解压测试结果,因此不会重新运行任何内容。简而言之,这些 sn-ps 正在创建一个非常昂贵的空操作。

如果你现在想“好吧,我也会停用缓存”,让我告诉你为什么不应该这样做。

然后,作者继续解释为什么重新运行一些测试是浪费时间:

您的绝大多数测试应该是确定性的,即给定相同的输入,它们应该产生相同的结果。

在少数情况下,您确实希望在代码未更改的情况下重新运行测试,您应该将它们建模为输入。以下是博客文章中的两个示例,它们显示了添加输入以便任务在其最新检查期间使用它。

task randomizedTest(type: Test) 
  systemProperty "random.testing.seed", new Random().nextInt()


task systemIntegrationTest(type: Test) 
  inputs.property "integration.date", LocalDate.now()

我建议阅读整篇博文。

【讨论】:

这对于您正在谈论的特定用例来说听起来很棒,但我正在针对实时外部 Web 服务编写部署后测试,并且恰好使用 junit 和 gradle 来完成此操作。被测代码并不存在于 repo 中,实际上 is 没有“应用程序代码”,因为我实际上是在测试实时生产系统而不是代码本身。感谢您的回答,这非常有用!只是想指出,即使没有任何代码 gradle 知道正在发生变化,也有其他用例需要每次都重新运行测试 作者缺少测试的基本概念:非确定性行为。如果您怀疑测试是可以的,但代码有一些错误并且导致结果不确定,那么,如果您在第一次运行时碰巧成功,那么根据这个短浅的建议,您将永远不会获得堆栈跟踪。测试应该运行你想要的次数。考虑到运行多个测试的不良做法是完全错误的,并且违背了测试概念。【参考方案5】:

--rerun-tasks 有效,但效率低,因为它会重新运行所有任务。

cleanTest 本身可能不够,因为构建缓存。

所以,最好的方法是:

./gradlew --no-build-cache cleanTest test

【讨论】:

【参考方案6】:

这是使用“build.gradle”文件的解决方案,以防您不想修改命令行:

test 
    dependsOn 'cleanTest'
    //Your previous task details (if any)

这是输出。请注意与您之前的输出相比的 2 处更改:

1) 一个新的“cleanTest”任务出现在输出中。

2) 'test' 总是被清理(即从不'UP-TO-DATE')所以它每次都会被执行:

$ gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:findMainClass
:jar
:bootRepackage
:assemble
:cleanTest
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
:check
:build

【讨论】:

test 之前运行cleanTest 不会重新运行测试,它会清理它们的输出,但测试任务仍会从缓存中获取测试结果 - 请参阅 github.com/gradle/gradle/问题/9153【参考方案7】:

另外, 必须添加--rerun-tasks 真的是多余的。永远不会发生。创建--no-rerun-tasks 并在cleanTask 时将--rerun-tasks 设为默认

【讨论】:

【参考方案8】:

TL;DR

test.dependsOn cleanTest

【讨论】:

根据***.com/a/52484259/466862 是行不通的。 好吧,gradle 文档有点令人困惑......这里他们说 cleanTest 可以用于此目的。 docs.gradle.org/current/userguide/…。而且,它适用于我的机器(和 gradle 版本 4.10.3);)【参考方案9】:

以上方法都不适合我。 对我有用的只是从 /Users/<username>/.gradle/caches/build-cache-1/ 的缓存目录中删除所有项目。

【讨论】:

以上是关于当所有测试都是最新的时如何运行 Gradle 测试?的主要内容,如果未能解决你的问题,请参考以下文章

如何运行我所有的 PHPUnit 测试?

主要来源中的 Gradle 测试

gradle测试任务相互冲突

如何使用 Gradle 运行多个命名测试?

如何使用 Gradle 运行 JUnit 测试?

五分钟教程:如何在Docker当中运行平行测试