What is the expected behavior of `Test` task up-to-date checking in Kotlin JVM and Kotlin Multiplatform?

I have a project that uses the kotlin jvm and kotlin multiplatform gradle plugins. The Test tasks seem to never be UP-TO-DATE, even when the compile tasks are.

So, in other words, when I run builds the compile tasks sometimes are skipped but the test tasks are not.

For example, I have a jvm subproject with the path :k:fx:control. If I re-run a build, the compile task is not executed because it is up-to date. However, the test task runs:

Task ':k:fx:control:test' is not up-to-date because:
  Task.upToDateWhen is false.

I have not manually made changes to the upToDateWhen property of my test tasks. However, I do not know if any of my other gradle configuration code or gradle plugins modify that property.

So my question is: what is the expected behavior here? By default, is it expected that Test tasks run even when compile tasks are up to date? Or is this happening because of something in my configuration or plugins?

If I want to make Test tasks use up-to-date checking so that they do no always run, what is the recommended way to do this? My desired behavior is that tests run if and only if the code has just been re-compiled.

Also adding to my confusion: It seems that outputs.upToDateWhen contains multiple predicates, and if even a single one evaluates to false then, then the task not be considered up-to-date.

I believe that my test tasks had some upToDateWhen Spec(predicate) added that is always evaluating to false. But I cannot find where it is defined, and cannot figure out how to remove it. So appending my own upToDateWhen Spec seems like it will do nothing, since this other one will always evaluate to false anyway.

At this point I don’t know any way around this issue.

Yes, you are right.
If one of the upToDateWhen specs evaluates to false, the task is considered out-of-date.
This is not a standard Gradle behavior, if nothing changed, test tasks should be up-to-date.
So it is most probably either your build or some plugin you apply that causes this behavior.

I’d add breakpoints to the two upToDateWhen methods in TaskOutputs and then run the build through a debugger. Then you should see where those specs are added from.

I’ve followed your advice and used breakpoints to diagnose the issue.

I discovered that there is some generated code that is doing the following:

//file:noinspection GrPackage

String[] ijTestIncludes = []

Class abstractTestTaskClass = null
try {
  abstractTestTaskClass = Class.forName("org.gradle.api.tasks.testing.AbstractTestTask")
} catch (ClassNotFoundException ex) {
 // ignore, class not available

gradle.taskGraph.whenReady { taskGraph ->
  taskGraph.allTasks.each { Task task ->
    if (task instanceof Test || (abstractTestTaskClass != null && abstractTestTaskClass.isAssignableFrom(task.class))) {
      try {
        task.outputs.upToDateWhen { false }
        String[] strings = ['*']
        if(ijTestIncludes.size() > 0 && ijTestIncludes != strings) {
          def filter = task.getFilter()
          filter.setIncludePatterns(new String[0])
          ijTestIncludes.each() {
            filter.includeTestsMatching "${it}"
      catch (all) {
        logger.error("", all)

Note the line task.outputs.upToDateWhen { false } is applied to all test tasks.

This snippet appears from the IntelliJ debugger in my IntelliJ editor under a tab called “ijtestinit”. It is not a regular file on my file system, as when I use the “select file in project view” action, nothing is selected (and this action works all other times).

From googling that name, I found “ijtestinit” is created here: intellij-community/GradleExecutionHelper.java at b880fce3d204d3564a07bcee079d1b08ca7843c8 · JetBrains/intellij-community · GitHub

Given this information, I hope that someone with more knowledge about the interactions between IntelliJ and Gradle can advise me on how to make my test tasks properly up to date when they should be.

The ijtestinit is an init script generated by IntelliJ and injected into your build when you run tests from IntelliJ as you told IntelliJ to run the tests it injects into the tasks to always be out-of-date so that the tasks rerun.

If you run your build from the commandline or probably also when you tell Gradle to run the test task or check task or whatever, I’d exect IntelliJ to not inject this logic. But if you tell IntelliJ to “run these tests”, it sounds appropriate that it also cares for the tests to actually be run.