Search code examples
androidunit-testingjunitjunit4android-testing

JUnit Local Test - Android Context 'No instrumentation registered!'


Expected

Access the Android Context within a local JUnit test as outlined in the Android Documentation Build local unit tests example MyLocalUnitTestClass.

Observed

Runtime Error

java.lang.IllegalStateException: No instrumentation registered! Must run under a registering instrumentation.

Implementation

The attempted implementation may also be found at the Coinverse Open App GitHub project under the test/poc branch.

Directory - app > src > test > java

ExampleUnitTest.kt

package app.coinverse

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import org.junit.Assert.assertEquals
import org.junit.Test

class ExampleUnitTest {

    val context = ApplicationProvider.getApplicationContext<Context>()

    @Test
    fun addition_isCorrect() {
        FirebaseHelper.initialize(context)
        assertEquals(4, 2 + 2)
    }
}

build.gradle (Module: app)

The library dependencies are based both off of the Set up your testing environment documentation and the Android Testing Codelab build.gradle configuration. The only test type implemented is the Local Unit test. However, included are dependencies for Android Unit and Instrumented tests just in case.

Below is the code / libraries related to testing.

android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 24
        targetSdkVersion 28
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    sourceSets {
        androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
    }

    // Gradle automatically adds 'android.test.runner' as a dependency.
    useLibrary 'android.test.runner'
    useLibrary 'android.test.base'
    useLibrary 'android.test.mock'

    testOptions {
        unitTests.includeAndroidResources = true
    }
}

dependencies {
    // Testing

    // Local Unit
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.mockito:mockito-core:2.19.0'
    testImplementation "org.hamcrest:hamcrest-all:1.3"
    testImplementation "androidx.arch.core:core-testing:$lifecycle_version"
    testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1"
    testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.1"
    testImplementation "org.robolectric:robolectric:4.3"
    testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    testImplementation 'androidx.test.espresso:espresso-contrib:3.2.0'
    testImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
    testImplementation 'com.google.truth:truth:0.44'

    // Android Unit
    androidTestImplementation 'junit:junit:4.12'
    androidTestImplementation 'org.mockito:mockito-core:2.19.0'
    androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:2.12.1"
    androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.1"

    // AndroidX - JVM
    testImplementation "androidx.test:core-ktx:1.2.0"
    testImplementation "androidx.test.ext:junit-ktx:1.1.1"
    testImplementation "androidx.test:rules:1.2.0"
    implementation "androidx.fragment:fragment-testing:1.2.0-alpha02"
    implementation "androidx.test:core:1.2.0"
    implementation "androidx.fragment:fragment:1.2.0-alpha02"

    // Instrumented testing
    androidTestImplementation "androidx.test:core-ktx:1.2.0"
    androidTestImplementation "androidx.test.ext:junit-ktx:1.1.1"
    androidTestImplementation "androidx.test:rules:1.2.0"
    androidTestImplementation "androidx.room:room-testing:2.1.0"
    androidTestImplementation "androidx.arch.core:core-testing:2.0.1"
    androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0"
    androidTestImplementation "androidx.test.espresso:espresso-contrib:3.2.0"
    androidTestImplementation "androidx.test.espresso:espresso-intents:3.2.0"
    androidTestImplementation "androidx.test.espresso.idling:idling-concurrent:3.2.0"
    androidTestImplementation "org.robolectric:annotations:4.3"
    implementation "androidx.test.espresso:espresso-idling-resource:3.2.0"

    // Resolve conflicts between main and test APK:
    androidTestImplementation "androidx.annotation:annotation:1.1.0"
    androidTestImplementation "androidx.legacy:legacy-support-v4:1.0.0"
    androidTestImplementation "androidx.recyclerview:recyclerview:1.0.0"
    androidTestImplementation "androidx.appcompat:appcompat:1.0.2"
    androidTestImplementation "com.google.android.material:material:1.0.0"

    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

Attempted Solutions

  • File > Invalidate Caches and Restart...
  • Initiate the Context before running the test resulting in the same runtime error.

ExampleUnitTest.kt

@Before
fun setup() {
    val context = getApplicationContext<Context>()
}
  • Move the ExampleUnitTest.kt class into a sharedTest directory as outlined in the Running your first test section of the Testing Codelab.

Directory - app > src > sharedTest > java

build.gradle (Module: app)

android {
        sourceSets {
            String sharedTestDir = 'src/sharedTest/java'
            test {
                java.srcDir sharedTestDir
            }
            androidTest {
                java.srcDir sharedTestDir
            }
        }
}

Addendum

Full Error Message

java.lang.IllegalStateException: No instrumentation registered! Must run under a registering instrumentation.

at androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(InstrumentationRegistry.java:45)

at androidx.test.core.app.ApplicationProvider.getApplicationContext(ApplicationProvider.java:41)

at app.coinverse.ExampleUnitTest.setup(ExampleUnitTest.kt:29) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)

at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)

at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)

at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)

at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)

at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)

at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)

at org.junit.runners.ParentRunner.run(ParentRunner.java:363)

at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)

at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)

at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)

at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Solution

  • Solution

    Refactor AndroidViewModel implementation to ViewModel as outlined in Jose Alcérreca's post Locale changes and the AndroidViewModel antipattern

    This refactor will remove the need to create Application Context.

    In addition to the ViewModel refactor, pass components into the ViewModel as an argument to separate dependencies. A Dependency Injection library may be used in the new ViewModel in order to create required components (ie — Repository, Database, Analytics, and etc.) and de-couple them from the ViewModel as well.