Custom Gradle task to execute all unit tests in all modules

168 Views Asked by At

I would like to write a custom task for an Android application project with multiple Gradle modules which executes the unit tests in the following modules so I can run all of them with one Gradle task invocation:

  • Android application module, e.g. testOrangeDebugUnitTest
  • Android library modules, e.g. testDebug
  • Java/Kotlin library modules, e.g. test

Requirements

  1. It would be convenient to being able to configure one product flavor (e.g. “orange”) to being tested (running all would take too long).
  2. Also, running Android unit tests with the debug product type is sufficient.
  3. Modules names should not be hardcoded so that the project is extensible and tests in new modules are picked up automatically.

Draft

Here is my work in progress approach:

// buildSrc/src/main/kotlin/com/example/TestAllModulesTask.kt
abstract class TestAllModulesTask : DefaultTask() {

    private companion object {
        val DISALLOWED_BUILD_TYPES = setOf("release")
        val DISALLOWED_PRODUCT_FLAVORS = setOf("blue", "green")
    }

    private val allowedTestTasks = mutableListOf<Pair<Project, Test>>()

    init {
        project.subprojects
            .forEach { subproject ->
                subproject.tasks
                    .filterIsInstance<Test>()
                    .filter { !it.namedWithAny(DISALLOWED_BUILD_TYPES) }
                    .filter { !it.namedWithAny(DISALLOWED_PRODUCT_FLAVORS) }
                    .forEach { task ->
                        println(":${subproject.name}:${task.name}")
                        allowedTestTasks.add(subproject to task)
                    }
            }
    }

    override fun getDescription(): String {
        return "Runs all tests for all modules except for disallowed build types and product flavors."
    }

    override fun getGroup(): String {
        return "verification"
    }

    @TaskAction
    fun action() {
        println(allowedTestTasks)
        // TODO Execute all the allowedTestTasks with ./gradlew
    }

    private fun Test.namedWithAny(disallowedTerms: Set<String>): Boolean {
        return disallowedTerms.any { name.contains(it, ignoreCase = true) }
    }

}

Then register the custom task:

// /build.gradle
import com.example.TestAllModulesTask

tasks.register("testAllModules", TestAllModulesTask)

Questions

  1. What would be a modern way to collect all the test tasks and wire them with the custom task? I don't really like the string comparison here. Filtering by build type and product flavor would be appreciated.
  2. How do I tell the custom task to actually execute all the test tasks which were collected?

Related

1

There are 1 best solutions below

0
On
tasks.register("specialTest") {
}

gradle.projectsEvaluated {
    subprojects {
        var subTask = rootProject.findProject(":${it.name}").tasks.named("test")
        rootProject.tasks.specialTest.dependsOn subTask
    }
}

Rudimentary, but effective. It creates the task you want to run, then loops over sub-modules (which can be filtered with an if statement), and applies the sub-module's task to the created task. Works in Gradle 8.5