Gradle modules: Running unit tests only in affected modules

Leandro Borges Ferreira
ITNEXT
Published in
4 min readSep 2, 2020

--

1 — Intro

Hi.

Multimodular projects are becoming more popular in the Android world, especially after Google IO of 2019, where Google introduced this idea and showed the benefits of it.

Although modules are becoming more popular, there's still a lot to be done in the tooling to analyse and deal with then. It is hard to find tools to analyse how good is the distribution of code between the modules, how the compilation is made faster by the then, visualize the dependencies between modules and to look for possible optimizations.

So, in this article, I would like to teach you how to make an interesting project to help you to analyse your modular Gradle project (It is important to say that it applies to any Gradle project, this is not limited to android at all). The goal is to:

1- Print the affected modules by a branch.

2- Run unit tests only in the affected modules.

3- Run UI tests on in the affected modules. (This comes in a future article)

4- Visualize the graph of dependencies and study how the related to each other. (This also comes on a future article)

This can help you and your team to avoid running unit tests on modules that were not changed by a PR and speed up your CI, understand how the modules interact with each other and how complex this graph is at the moment. But enough with this introduction, let's have some fun!

2 — Finding the affected modules

For this problem, I created a Gradle plugin that you can use and print the adjacency list of your modules (By the way, if you don't know what an adjacency list is: Is it a way to represent a graph, you can search for it and there will be a lot of references). You can find the documentation and the code for the plugin here: https://github.com/leandroBorgesFerreira/dag-command. It is based on the amazing work of Pedro Rodriguez.

Let's suppose that we have a modular app, like this one:

Image 1 — Example of module app.

You can take a look at the small project here: https://github.com/leandroBorgesFerreira/FoodList

So, 6 modules and they have some dependency between each other, we will understand how they depend on each other later in this article.

You can add the library in this way. The library in the project level Gradle file.

build.gradle.kts (Kotlin version):

The version might be outdated!

build.gradle (Groovy version):

The version might be outdated!

Then you can make a small change (just change any line of the code, like adding a comment) in the core module and I run the plugin:

./gradlew dag-command

You can go to the folder ./build/dag-command/ and you'll see some files. There you'll find adjacencies-list.json. This file contains information about the graph of dependencies of the modules of your app. In the case of the Image 1 example, this is the output that I got:

{“app”:[],”core”:[“showfood”],”data”:[“app”,”showfood”],”domain”:[“data”,”showfood”],”search”:[“showfood”],”showfood”:[“app”]}

which can be formatted to:

{
"app": [],
"core": [
"showfood"
],
"data": [
"app",
"showfood"
],
"domain": [
"data",
"showfood"
],
"search": [
"showfood"
],
"showfood": [
"app"
]
}

And you can see the affected modules by looking in the file affected-modules.json. That's my output:

["app","core","showfood"]

You can take a look at the adjacency list and conclude that changing the core will affect app, showfood and core because the dependency is core -> showfood -> app.

Ok. So we made it! Now we know how to create the affected modules by our PR! But… that's not very useful… right? We need to use this information to create something more, like speed up our tests or analyse how our modules are connected (the later will come in another article, sorry =P). This leads us to the next part of the article.

3 — Running unit tests only for changed modules

What we need to do now is to create a command that will run the unit tests only for the modules that were changed by the current branch.

I use Python for my day to day automation, but you can use any language that you prefer. You can parse the affected modules to a simple command with not much trouble:

You can use whatever you want, including this =P

This snippet will produce this Gradle command:

./gradlew core:testDebug showfood:testDebug app:testDebug

So the modules data, domain and search are not tested because our change did not affect then, so running unit tests on those modules would be just a waste of time.

4 — I hope you enjoyed it =]

Now you can enjoy some extra time on your CI. Maybe you can drink one extra cup of coffee, have some conversation with your colleagues, read a small part of a book… you name it!

If this article and the library helped you somehow (or if you think this is an interesting text) please remember to click that clap button and give me a little star on Github. That helps a lot!

Thanks!

--

--