Full mocked android application using Build Flavors and dependency injection
When developing Android applications, one of the biggest challenges developers face is working with incomplete or unavailable backend services or third-party libraries. This can slow down development, especially when building MVPs (Minimum Viable Products) or testing new features. To address this, we can use Build Flavors to create two versions of the app: one with mock implementations and another with real implementations. This approach allows developers to:
Develop features independently of backend availability: Use mock data to simulate API responses.
Mock third-party libraries: Skip time-consuming steps like scanning an ID card or making payments during development.
Test features in isolation: Ensure functionality works as expected without relying on external services.
In this article, we’ll walk through how to set up an Android application with mock and real build flavors, and how to use Dependency Injection (DI) to switch between mock and real implementations seamlessly.
1. Why Use Mock and Real Build Flavors?
Using mock and real build flavors provides several benefits:
Faster Development: Developers can continue working on the app even if the backend or third-party services are not ready.
Cost Efficiency: Avoid unnecessary costs (e.g., API calls, third-party service usage) during development.
Improved Testing: Test features in isolation using mock data, ensuring they work correctly before integrating with real services.
Flexibility: Switch between mock and real implementations easily, depending on the development or testing phase.
2. Setting Up Build Flavors
Build flavors allow you to create different versions of your app from a single codebase. For our use case, we’ll define two flavors: mock and real.
In your app module (Or library module) build.gradle
file, define the mock
and real
flavors:
android {
...
flavorDimensions "environment" // or any name you want
productFlavors {
mock {
dimension "environment"
applicationIdSuffix ".mock"
}
real {
dimension "environment"
applicationIdSuffix ".real"
}
}
}
After adding this to the android block of build.gradle
file, you should synch your project with gradle.
Click on the following button and wait for gradle to finish synching.
Once finished, you can select the flavor:
In Android Studio, go to the View menu. Then select Tool Windows > Build Variants.
You can find the Build Variants tab on the left side of the IDE.clicking on Build Variants button in Android studio, which will show the following popup.
Here i’ll use the second option:
Then click on the dropdown button and select the flavor you want to run the app on:
3. Dependency Injection with Hilt
To switch between mock and real implementations, we’ll use Hilt (you can use koin if you want), a dependency injection framework for Android. Hilt makes it easy to provide different implementations for the same interface based on the build flavor.
Setting Up Hilt: Add the following dependencies to your app module build.gradle
file:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
implementation "com.google.dagger:hilt-android:2.48"
kapt "com.google.dagger:hilt-compiler:2.48"
}
Annotate your Application
class with @HiltAndroidApp
:
@HiltAndroidApp
class MyApplication : Application()
4. Defining Mock and Real Implementations
Let’s assume we have an AuthRepository
interface with two implementations: AuthRepositoryMockImpl
and AuthRepositoryRealImpl
. We’ll use Hilt to provide the appropriate implementation based on the build flavor.
Step 1: Define the Interface
interface AuthRepository {
fun login(username: String, password: String): Boolean
fun scanIdCard(): Boolean
}
Step 2: Create Mock Implementation
First Select the mock flavor (explained in step 1)
Then in the app module, right click on the src folder then select New then select directory
The in the next popup write mock/k and select mock/kotlin
Then inside kotlin folder create the package of your app (for me it’s com.zrcoding.apps) and create these two files inside it:
class AuthRepositoryMockImpl : AuthRepository {
override fun login(username: String, password: String): Boolean {
// If you or your team want to see the loading
delay(1.seconds)
return true
}
override fun scanIdCard(): Boolean {
// If you or your team want to see the loading
delay(1.seconds)
return true
}
}
@Module
@InstallIn(SingletonComponent::class)
abstract class AuthModule {
@Binds
@Singleton
abstract fun bindAuthRepository(authRepositoryMockImpl: AuthRepositoryMockImpl): AuthRepository
}
Step 3: Create Real Implementation
First Select the real flavor (explained in step 1)
We’ll follow the same steps, but this time we’ll write real/k and choose real/kotlin folder and create the package folders (for me it’s com.zrcoding.com) and past these two files inside it:
// TODO In your app you’ll need to add error handling.
class AuthRepositoryImpl @Inject constructor(
private val apiService: ApiService,
private val thirdPartyLibrary: ThirdPartyLibrary
) : AuthRepository {
override fun login(username: String, password: String): Boolean {
// Make a real API call to authenticate the user
return apiService.login(username, password)
}
override fun scanIdCard(): Boolean {
return thirdPartyLibrary.scanIdCard() ...
}
}
@Module
@InstallIn(SingletonComponent::class)
abstract class AuthModule {
@Binds
@Singleton
abstract fun bindAuthRepository(authRepositoryImpl: AuthRepositoryImpl): AuthRepository
}
in the end you’ll end up with the following structure:
4. Running the Application in Mock and Real Flavors
Once you’ve set up your Android application with mock and real build flavors and adding mock and debug implementations, the next step is to run the app in each flavor to test and verify the behavior. Running the app in different flavors is straightforward . Just select the flavor you want and run the application and Hilt will choose the right implementation for you.
5. Conclusion
By combining Build Flavors and Dependency Injection, you can create a flexible, scalable, and maintainable Android application. Build flavors allow you to manage multiple environments and app variants, while Hilt simplifies dependency management and promotes clean architecture.
Whether you’re building a small app or a large-scale project, these techniques will help you deliver high-quality software with ease.
Happy coding!
Zouhir RAJDAOUI