Android Gradle plugin with Test Fixtures support

🇺🇦 Eugen Martynov
4 min readJan 7, 2022

16 May 2024 UPDATE

I wrote an article about the test fixtures setup in the Android project.

12 May 2024 UPDATE

The Google team is working to land it in AGP 8.5. I’ve tried the latest (for this time) AGP 8.5.0-beta01, and it is working NOW!

I’ve updated the test project. I will write a clean article on how to use test fixtures in your project.

10 March 2024 UPDATE

The Google team is working to land it in AGP 8.4. I’ve tried the latest (for this time) AGP 8.4.0-alpha13 and it is not working yet.

10 May 2022 UPDATE

The plain kotlin modules just work. The issue is only with android modules right now.

15 February 2022 UPDATE

The Android project deals with Kotlin Android plugin also. And, unfortunately, Kotlin test fixtures are not supported there fully yet. You can follow JetBrain ticket.

I want to thank Nelson Osacky, Danny Preussler and Jeroen Mols for proofreading and comments on improving this article.

Photo by Sergey Zolkin from Unsplash

(The main purpose of the example source in the article is to demonstrate the problem with sharing test sources or resources between modules)

Assume that you have a module that deals with user data — users. It probably has a class like

data class User (   val userId: String,
val firstName: String,
val lastName: String,
val email: String,
...
)

And you have another module — login — where it shows the user first name on the lock screen — “Hi <user.firstName>, please log in to see your app personal data”.

While you’re writing the tests around the login, you probably mock/fake the dependencies from the user module. And you have to create quite often different kinds of users with slightly changed properties. Usually, we have helper functions like:

fun createUser (   userId: String = "test_user_id",
firstName: String = "James",
lastName: String = "Bond",
...
) = User (
userId = userId,
firstName = firstName,
lastName = lastName,
...
)

If you call this function, you get some default constructed user. It is also flexible so that you can change one property that is important for your current test. This function belongs to the test source of the login module where you’re writing tests. BTW, such code (and/or resources) you want to share between tests of different modules is called Test Fixtures.

The problem comes when you have more modules depending on the user module and you want to use the same test fixtures in these modules as well. Unfortunately, the test code is accessible from the module that it belongs. There are a few solutions to this like duplicating such functions to other modules or creating a sophisticated modules’ structure. However, it is not scalable well (at least from my experience).

We have a solution now supported by our tooling.

Gradle released test fixtures support for Java modules in the past.

However, our Android modules deal with Android Gradle plugin (I use AGP later in this article) and not with Java modules.

AGP 7.0 API has class TestFixtures, and I started checking what is already possible to do with test fixtures in the project. However, support is limited. You can already use Java test fixtures via the Gradle command-line build. The latest (Chipmuk alpha06 version) Android Studio can also autocomplete Kotlin test fixture classes. However, the further test run will fail with compilation. Hopefully, full support with Kotlin and lint will land in AGP 7.1 version.

Yeah, it is a painful experience now without Kotlin support. But you can start with Java test fixtures and convert them to Kotlin later.

To ship test fixtures from your library, you must use the latest canary AGP. And enable it first in the build.gradle:

android {   testFixtures {
enable = true
}
}

Create test fixtures source set in the current module (again notice only Java now):

/module
/src
/main
/testFixtures
/<package>
UserDBFixtures.java

Create a fixture, for example:

public class Fixture {
public static UserDB defaultUser = new UserDB("Test Name");
}

After this step you can create fixtures binary

./gradlew :<module>:assembleDebugTest

After this, you can find the additional artifact <module>-debug-testFixtures-testFixtures.aab in the build output folder and if you check inside you will see the test fixture code inside. If you’re a library creator, you can also distribute this artifact over maven.

To use test fixtures in the another module you define it in the Gradle as dependency:

dependencies {   testImplementation testFixtures(project(":module"))}

And after you can reference it in your tests:

class SomeUniteKtTest {
@Test
fun `Some test`() {
val user = Fixture.defaultUser
}
}

That is it!

You can also follow the Google issue ticket for updates.

You can find my test project with fixture usage here. The database module provides test fixtures and the core is consuming them.

Credits to Lukáš Sztefek(https://github.com/skywall) for his multi-module project (https://github.com/skywall/android-dagger-multimodule) that I used as the start for testing the test fixtures feature.

I will keep the article updated while the tooling is progressing.

If you like the article, you can follow me on twitter.

Best!

--

--

🇺🇦 Eugen Martynov

Stand with Ukraine! The loving XP husband and freelance Android/Kotlin engineer.