Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running a subset of unit tests when source file changes using Gradle

I use the gradle 3.5 build system and have several Unit and Integration tests in a Java project. When I make changes in the source files (sourceSets), gradle's compilation avoidance and incremental compilation makes sure only the relevant source files are compiled, which is a huge timesaver. However, all the tests run for every change made in the sourceSet. Can gradle identify and run only the relevant subset of Unit tests instead of all? If not, is there a way to achieve this?

like image 250
Abhishek Balaji R Avatar asked Apr 21 '17 00:04

Abhishek Balaji R


People also ask

How do you run a test case using Gradle command?

You can do gradle -Dtest. single=ClassUnderTestTest test if you want to test single class or use regexp like gradle -Dtest. single=ClassName*Test test you can find more examples of filtering classes for tests under this link.


1 Answers

Yes, Gradle can do that, but AFAIK not out of the box.

Gradle task can tell which files have been changed if the task is implemented as an Incremental Task. The Test task in Gradle is not incremental. Fortunately it's easy to turn it into one by extending it:

class TestWatcher extends Test {

    @TaskAction
    void executeTests(IncrementalTaskInputs inputs) {
        if (inputs.incremental) {
            def outputDir = this.project.sourceSets['test'].output.classesDir.absolutePath
            this.filter.includePatterns = []
            inputs.outOfDate { InputFileDetails change ->
                def candidate = change.file.absolutePath
                if (candidate.endsWith('.class')) {
                    candidate = candidate
                            .replace('.class', '')
                            .replace(outputDir, '')
                            .substring(1)
                            .replace(File.separator, '.')
                    this.filter.includePatterns += candidate
                }
            }
        }
        super.executeTests()
    }
}

This task is incremental (the main method takes the IncrementalTaskInputs as its argument). If the inputs are not incremental, it simply runs the original task. If the inputs are incremental, it iterates through the changes and sets up the includePatterns to include all classes. This will then run only the tests that have been changed.

You can use this task in your build.gradle:

task testWatcher(type: TestWatcher) {
}

It accepts all of the default test task configuration. You can place the task code directly in the script or under the buildSrc folder.

For more information you can see this article I wrote. It shows how to use this with the continuous build support to re-run only the changed tests whenever the code changes. The code from the article can be found in GitHub repo, where you will find the above code as well in a self-contained example.

like image 184
MartinTeeVarga Avatar answered Oct 02 '22 03:10

MartinTeeVarga