Kevin Worth

03/16/2023, 2:53 PM
Why might injection work just fine in app but not in instrumented tests?
import androidx.lifecycle.viewmodel.compose.viewModel
fun PrimaryScreen(viewModel: PrimaryViewModel = viewModel()) {...}

class PrimaryViewModel @Inject constructor(
    private val jokeRepository: JokeRepository
) : ViewModel() {...}

// app/src/main/java/com/example/jokes/data/di/DataModule.kt
interface DataModule {

    fun bindsJokeRepository(
        jokeRepository: DefaultJokeRepository
    ): JokeRepository

// app/src/androidTest/java/com/example/jokes/testdi/TestDataModule.kt
    components = [SingletonComponent::class],
    replaces = [DataModule::class]
interface FakeDataModule {

    abstract fun bindRepository(
        fakeRepository: FakeJokeRepository
    ): JokeRepository

class PrimaryScreenTest {

    var hiltRule = HiltAndroidRule(this)
    val composeTestRule = createAndroidComposeRule<ComponentActivity>()

    val rule: RuleChain = RuleChain

    fun setup() {
        composeTestRule.setContent {
            PrimaryScreen()               // Doesn't work
            // PrimaryScreen(viewModel()) // Also doesn't work
When I try to run my tests I get:
java.lang.RuntimeException: Cannot create an instance of class com.example.jokes.ui.joke.PrimaryViewModel
Caused by: java.lang.NoSuchMethodException: com.example.jokes.ui.joke.PrimaryViewModel.<init> []
Any ideas from anyone?
For those playing along at home, this(the following git diff) appears to work around the issue:
- fun PrimaryScreen(viewModel: PrimaryViewModel = viewModel()) {

+ fun PrimaryScreen() {
+     val viewModel: PrimaryViewModel = viewModel()
Now I wonder if anyone knows why… (again, with the key point being, if it works in the production code, why not in instrumented tests…)