------------------------- | Repository Test Screen | |-----------------------| | Data: | | [Loading...] | | | | [Fetch Data Button] | -------------------------
Repository testing with fakes in Android Kotlin - Mini App: Build & Ship
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch // TODO: Define Repository interface here // TODO: Implement FakeRepository here class RepositoryViewModel(private val repository: Repository) : ViewModel() { private val _data = MutableStateFlow<List<String>?>(null) val data: StateFlow<List<String>?> = _data fun fetchData() { viewModelScope.launch { // TODO: Fetch data from repository and update _data } } } // TODO: Build a Composable or Activity UI with a TextView and Button // TODO: Connect ViewModel to UI and show loading and data states
import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import androidx.lifecycle.viewModelScope interface Repository { suspend fun fetchItems(): List<String> } class FakeRepository : Repository { override suspend fun fetchItems(): List<String> { return listOf("Apple", "Banana", "Cherry") } } class RepositoryViewModel(private val repository: Repository) : ViewModel() { private val _data = MutableStateFlow<List<String>?>(null) val data: StateFlow<List<String>?> = _data fun fetchData() { viewModelScope.launch { _data.value = null // show loading val items = repository.fetchItems() _data.value = items } } } class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: android.os.Bundle?) { super.onCreate(savedInstanceState) val repo = FakeRepository() val factory = object : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") return RepositoryViewModel(repo) as T } } setContent { MaterialTheme { Surface(modifier = Modifier.padding(16.dp)) { val viewModel: RepositoryViewModel = viewModel(factory = factory) RepositoryTestScreen(viewModel) } } } } } @Composable fun RepositoryTestScreen(viewModel: RepositoryViewModel) { val data by viewModel.data.collectAsState() Column { Text(text = "Data:") Text(text = data?.joinToString(", ") ?: "Loading...") Button(onClick = { viewModel.fetchData() }, modifier = Modifier.padding(top = 16.dp)) { Text("Fetch Data") } } }
This solution defines a Repository interface with a suspend function fetchItems(). The FakeRepository returns a fixed list of fruits.
The RepositoryViewModel holds a MutableStateFlow to track the data list or loading state. The fetchData() function launches a coroutine to fetch data asynchronously and update the state.
The MainActivity sets up the UI with Jetpack Compose. It creates the ViewModel with the fake repository and shows a simple screen with a Text showing "Loading..." initially or the fetched data, plus a button to trigger fetching.
This approach allows testing repository logic without real network or database, making tests fast and reliable.
------------------------- | Repository Test Screen | |-----------------------| | Data: | | Apple, Banana, Cherry | | | | [Fetch Data Button] | -------------------------
Practice
Solution
Step 1: Understand the role of a fake repository
A fake repository mimics the real repository's behavior but uses in-memory data instead of real data sources.Step 2: Identify the benefit in testing
This allows tests to run quickly and reliably without depending on databases or network calls.Final Answer:
To simulate data operations without accessing real databases or network -> Option DQuick Check:
Fake repository = simulate data without real sources [OK]
- Thinking fakes improve UI animations
- Confusing fakes with Android framework replacement
- Assuming fakes increase app size
UserRepository?Solution
Step 1: Recall Kotlin interface implementation syntax
Kotlin uses a colon (:) to implement interfaces, not keywords like implements or extends.Step 2: Match correct syntax
class FakeUserRepository : UserRepository { } uses:correctly to implementUserRepository.Final Answer:
class FakeUserRepository : UserRepository { } -> Option CQuick Check:
Kotlin interface implementation uses ':' [OK]
- Using Java keywords like implements or extends in Kotlin
- Confusing inheritance with interface implementation
- Omitting the colon ':'
println(fakeRepo.getUserCount()) output?
interface UserRepository {
fun getUserCount(): Int
}
class FakeUserRepository : UserRepository {
private val users = mutableListOf("Alice", "Bob")
override fun getUserCount() = users.size
}
val fakeRepo = FakeUserRepository()Solution
Step 1: Analyze the users list initialization
Theuserslist contains two elements: "Alice" and "Bob".Step 2: Understand getUserCount() behavior
The method returns the size of theuserslist, which is 2.Final Answer:
2 -> Option AQuick Check:
users.size = 2 [OK]
- Assuming empty list by default
- Confusing method return with list content
- Expecting compilation error due to interface
class FakeUserRepository : UserRepository {
private val users = mutableListOf<String>()
fun getUserCount() = users.size
}Solution
Step 1: Check interface method implementation
The interfaceUserRepositoryrequiresgetUserCount()to be overridden.Step 2: Verify method declaration
The method lacks theoverridekeyword, causing a compilation error.Final Answer:
Missing override keyword for getUserCount() -> Option AQuick Check:
Interface methods need 'override' in Kotlin [OK]
- Forgetting 'override' keyword
- Thinking mutable list is wrong here
- Assuming constructor is mandatory
data class User(val name: String, val age: Int). Which fake repository method correctly returns users older than or equal to the given age?Solution
Step 1: Understand filtering condition
The requirement is to get users with age greater than or equal tominAge.Step 2: Match correct filter predicate
fun getUsersByMinAge(minAge: Int) = users.filter { it.age >= minAge } usesit.age >= minAge, which matches the requirement exactly.Final Answer:
fun getUsersByMinAge(minAge: Int) = users.filter { it.age >= minAge } -> Option BQuick Check:
Filter with '>= minAge' for correct users [OK]
- Using '>' excludes users exactly at minAge
- Using '<=' returns younger users
- Filtering by name length instead of age
Ready to prove it?
Pass the test and this pattern moves unknown
