I am writing an Espresso test to test that my App Links are handled properly by my app. I've setup Android Studio and created a test that passes, but the problem is that the test suite hangs. I have created a class, LinkDispatcherActivity, that is responsible for parsing the incoming links and dispatching their data to the appropriate activity. The tests are setup to use the ActivityTestRule to launch LinkDispatcherActivity and give it an intent with the URL to be tested. The tests are running and passing, so I assume I've set everything up correctly (a challenge all on its own 😜).
I have tried using the new ActivityScenarioRule but can't figure out how to pass an intent to it.
Here's my test class:
import android.content.Intent
import android.net.Uri
import android.widget.TextView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
import gov.nih.nlm.wiser.R
import gov.nih.nlm.wiser.link.WiserLinkDispatcherActivity
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.instanceOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
#RunWith(AndroidJUnit4::class)
#LargeTest
class SubstanceLinkTest {
#get:Rule
val activityTestRule: ActivityTestRule<LinkDispatcherActivity>
= ActivityTestRule(LinkDispatcherActivity::class.java, false, false) // initialTouchMode: false, launchActivity: false
#Test
fun shouldOpenLinkDispatcherActivity() {
val i = Intent().apply {
data = Uri.parse("https://mydomain/action?data=338")
}
activityTestRule.launchActivity(i)
onView(allOf(instanceOf(TextView::class.java), withParent(withId(R.id.action_bar))))
.check(matches(withText("My Title"))) // Passes!!
}
}
Here's my added dependencies in by build.gradle:
dependencies {
// Core library
implementation 'androidx.test:core:1.2.0'
// AndroidJUnitRunner and JUnit Rules
implementation 'androidx.test:runner:1.2.0'
implementation 'androidx.test:rules:1.2.0'
// Assertions
implementation 'androidx.test.ext:junit:1.1.1'
implementation 'androidx.test.ext:truth:1.2.0'
implementation 'com.google.truth:truth:0.42'
// Espresso dependencies
implementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.android.support:multidex-instrumentation:1.0.3'
}
Finally, here is what Android Studio shows in it's Run panel:
Cheers!
Edit:
Forgot to add that I'm getting this error in Logcat:
2019-06-21 12:02:24.326 6311-6035/? E/cckz: *~*~*~ Channel {0} was not shutdown properly!!! ~*~*~*
Make sure to call shutdown()/shutdownNow() and wait until awaitTermination() returns true.
java.lang.RuntimeException: ManagedChannel allocation site
at cclc.<init>(:com.google.android.gms#17455040#17.4.55 (100700-248795830):1)
at cckz.<init>(:com.google.android.gms#17455040#17.4.55 (100700-248795830):2)
at ccdo.b(:com.google.android.gms#17455040#17.4.55 (100700-248795830):14)
at rzc.a(:com.google.android.gms#17455040#17.4.55 (100700-248795830):43)
at rzc.a(:com.google.android.gms#17455040#17.4.55 (100700-248795830):58)
at atbl.a(:com.google.android.gms#17455040#17.4.55 (100700-248795830):9)
at atbl.a(:com.google.android.gms#17455040#17.4.55 (100700-248795830):26)
at qbi.run(:com.google.android.gms#17455040#17.4.55 (100700-248795830):1)
at sgs.b(:com.google.android.gms#17455040#17.4.55 (100700-248795830):37)
at sgs.run(:com.google.android.gms#17455040#17.4.55 (100700-248795830):21)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at smq.run(Unknown Source:7)
at java.lang.Thread.run(Thread.java:764)
UPDATE: The test completes properly when run on a physical device. Still not sure what's causing the emulator to hang.
I would suggest using an #After annotation with a method called something like 'tearDown'. You could do final assertions here and clean up any databases or processes that need to end and possibly call publisher.shutdown(); and publisher.awaitTermination(1, TimeUnit.MINUTES) Hope this can help somewhat.
Check this thread out too:
[https://github.com/googleapis/google-cloud-java/issues/3648]
Related
I'm using compose3 and dolphin Android Studio on Gruda Linux. All updated!
I have a #Composable function called feed which just have a FilledTonalButton.
For the content of FilledTonalButton I'm giving the icons.rounded.Add
Whenever I'm building, it gives me
e: /home/kumar-p/Desktop/AndroidStudio/<app_name>/app/src/main/java/com/f/rateme/MainActivity.kt: (95, 27): Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val Icons.Rounded.Add: ImageVector defined in androidx.compose.material.icons.rounded
<app_name> = my app name, removed because of confidentiality.
However when I replace the Icon() with Text(), it works!
My dependencies:
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.3.1'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.compose.material3:material3:1.0.0-alpha02'
implementation "androidx.compose.material:material-icons-extended:$compose_version"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
My imports on MainActivity.kt
package com.<example>.rateme // <example> = my company name
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.*
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material3.*
//import androidx.compose.material.icons.Icons
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.<example>.<app_name>.ui.theme.AppTheme // <example> = my company name, <app_name> = name of curent app
The function:
#Composable
fun feed(page: String): String {
FilledTonalButton(
onClick = {/*TODO: Add onClick for feed - New Post*/},
enabled = true) {
Icon(
imageVector = Add,
"New Post"
)
}
return page // TODO: implement the changes of page variable
}
If you find any typo please update it or tell me to fix.
PS: This is my first time here and I have an almost zero experience on android development and Kotlin. I developed terminal apps and worked on ML kinda work in Python, C++, C. So I may need more information in explanation. I started learning Android Development a week ago only.
Edit: You can ask me any more information.
Peace
Just specify the actual imageVector like this,
Icon(
imageVector = Icons.Default.Add
"New Post"
)
but you will still get ambiguity error. Since based on your post it looks like you want to use material3, just remove this import
import androidx.compose.material.* // remove this
or if you still want to use them both, you'll then have to declare their fully qualified name on usage,
androidx.compose.material3.Icon(
imageVector = Icons.Default.Add,
"New Post"
)
I'm trying to run a local unit test that depends on the context, and was following this guide: https://developer.android.com/training/testing/unit-testing/local-unit-tests#kotlin and I set up my project like this (following this link : https://developer.android.com/training/testing/set-up-project ):
build.gradle(app)
android {
compileSdkVersion 28
buildToolsVersion '27.0.3'
defaultConfig {
minSdkVersion 21
targetSdkVersion 27
versionCode 76
versionName "2.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
useLibrary 'android.test.runner'
useLibrary 'android.test.base'
useLibrary 'android.test.mock'
}
testOptions {
unitTests.returnDefaultValues = true
unitTests.all {
// All the usual Gradle options.
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen { false }
showStandardStreams = true
}
}
unitTests.includeAndroidResources = true
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion", {
exclude group: 'com.android.support', module: 'support-annotations'
})
// Espresso UI Testing dependencies
implementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
testImplementation 'androidx.test:core:1.0.0'
// AndroidJUnitRunner and JUnit Rules
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'
// Espresso Assertions
androidTestImplementation 'androidx.test.ext:junit:1.0.0'
androidTestImplementation 'androidx.test.ext:truth:1.0.0'
androidTestImplementation 'com.google.truth:truth:0.42'
implementation 'androidx.multidex:multidex:2.0.0'
}
My espresso_version is espressoVersion = '3.1.0'
My test that is located in module-name/src/test/java/ looks like this:
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.instacart.library.truetime.TrueTime
import edu.mira.aula.shared.extensions.android.trueDateNow
import edu.mira.aula.shared.network.ConnectivityHelper
import kotlinx.coroutines.experimental.runBlocking
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.util.*
import java.util.concurrent.CountDownLatch
class TimeExtensionsUnitTest {
private lateinit var instrumentationCtx: Context
#Before
fun setup() {
instrumentationCtx = ApplicationProvider.getApplicationContext<Context>()
}
#Test
fun testTrueTimeValueReturnsIfInitialized() {
if (ConnectivityHelper.isOnline(instrumentationCtx)) {
runBlocking {
val countDownLatch = CountDownLatch(1)
TrueTime.build()
.withSharedPreferencesCache(instrumentationCtx)
.withConnectionTimeout(10000)
.initialize()
countDownLatch.countDown()
try {
countDownLatch.await()
val dateFromTrueTime = trueDateNow()
val normalDate = Date()
Assert.assertNotEquals(dateFromTrueTime, normalDate)
} catch (e: InterruptedException) {
}
}
}
}
Everytime I run it, it gives me:
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)
If I run it as a Instrumental Test(changing the package) it runs without errors.
But I thought that this guide was exactly to be able to run unit test using Android Framework classes such as Context.
I even tried run that class UnitTestSample but the same error occurs.
I also removed all android.support dependencies from my project
Any ideas on how to solve it?
Update
You should no longer encounter this error if youre using the latest gradle version.
I also encountered this issue.
If you look at migrating to Robolectric 4.0 here, it suggest to add the following line in your gradle.properties.
android.enableUnitTestBinaryResources=true
The problem is that, if you add this you your gradle.properties, it will output this warning:
WARNING: The option setting
'android.enableUnitTestBinaryResources=true' is experimental and
unsupported.
Now, if you look at Robolectric releases here. You could see that this is a known issue where they state that
Android Gradle Plugin may report the following warning, which may be safely ignored: WARNING: The option setting 'android.enableUnitTestBinaryResources=true' is experimental and unsupported.. Android Gradle Plugin 3.4 will resolve this issue.
I believe unless you could update you gradle to 3.4. You won't be able to solve this issue.
What I did instead was to include Robolectric 4.0 as dependency.
testImplementation "org.robolectric:robolectric:4.0.2"
and annotate my test class with
#RunWith(RobolectricTestRunner::class)
This should make your test work.
Now when you run the test, you'll notice that Robolectric will log the following:
[Robolectric] NOTICE: legacy resources mode is deprecated; see
http://robolectric.org/migrating/#migrating-to-40
Ignore this for now but as soon as you could update your gradle, migrate to the new Robolectric testing.
I follow the official guide also met this issue, fix it with below steps.
Add testImplementation in app build.gradle
// Required -- JUnit 4 framework
testImplementation 'junit:junit:4.13.1'
testImplementation 'androidx.test:core-ktx:1.3.0'
testImplementation 'androidx.test.ext:junit-ktx:1.1.2'
// Robolectric environment
testImplementation 'org.robolectric:robolectric:4.4'
// Optional -- truth
testImplementation 'androidx.test.ext:truth:1.3.0'
testImplementation 'com.google.truth:truth:1.0'
// Optional -- Mockito framework
testImplementation 'org.mockito:mockito-core:3.3.3'
The official guide missed two testImplementations
testImplementation 'androidx.test.ext:junit-ktx:1.1.2'
testImplementation 'org.robolectric:robolectric:4.4'
Add testOptions block in app build.gradle
android {
// ...
testOptions {
unitTests.includeAndroidResources = true
}
}
Add #RunWith(AndroidJUnit4::class) to your test class
Example:
import android.content.Context
import android.os.Build.VERSION_CODES.Q
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
#RunWith(AndroidJUnit4::class)
#Config(sdk = [Q])
class UnitTestWithContextDemoTest {
private val context: Context = ApplicationProvider.getApplicationContext()
fun test_getPackageName() {
assertThat(context.packageName).contains("your_package_name")
}
}
NOTE
#Config(sdk = [Q]) is required when your targetSdkVersion greater than 29. Because robolectric NOT support targetSdkVersion greater than 29.
Spend hours on similar issue, and the problem wasn't in dependencies, rather in AndroidStudio itself Based on the answer:
IDE tries to run local unit tests instead of instrumented
.
Make sure it's run as instrumented test (red is local tests, green - instrumented):
After added instrumented test for the class it's run as expected under instrumented. How I done this? 2 ways I found:
1) Edit configuration (as on the last screenshot) and adding function manually
2) Under Project tap (top left corner) I selected Tests instead of android, found the test, right click - create test. After this step all new tests are run under instrumented tests
I had similar error and was struggling a lot to fix it. My problem was that I was mixing AndroidJUnit4, InstrumentationRegistry, ApplicationProvider and AndroidJUnitRunnerversions / packages. Make sure they all are of the same generation. These are the classes that made it all run for me:
androidx.test.runner.AndroidJUnitRunner
androidx.test.platform.app.InstrumentationRegistry
androidx.test.ext.junit.runners.AndroidJUnit4
androidx.test.core.app.ApplicationProvider
for these I needed the following in the dependencies part of my build.gradle
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation "com.android.support:support-annotations:27.1.1"
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
And of course the correct
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
in my defaultConfig of the build.gradle
Next things are not mentioned on google test guide, but they are what I found:
androidx.test in case of unit tests is just an interface/api (I don't know what about instrumented tests) and it needs implementations, which is robolectric library.
That's why robolectric dependency is also required:
testImplementation "org.robolectric:robolectric:{version}"
#RunWith(AndroidJUnit4.class)
is required. To get nondeprecated class you need to add:
testImplementation "androidx.test.ext:junit:{version}". By the way this dependency has transitive junit4 dependensy.
Also you can faced with: Failed to create a Robolectric sandbox: Android SDK 29 requires Java 9 (have Java 8) in case you use java 8 and compileSdkVersion 29 or above. Here you can find how to deal with it.
Make sure to put your instrumentation tests (tests you run with a Runner) in androidTest and not just test
in my case I was using getApplicationContext() in a fragment from the test package, I just change it to getContext(). and this fixed my crash!
In my case, changing the test method name fixed the issue.
Most probably the Android Studio cached the methods with indexes that caused a problem.
[Note : To remember this ]
AndroidJUnit4.class | used to run testcases of intrumentation type inside source set called androidTest [which requires phone or emulator]
AndroidJUnit4ClassRunner | used to run testcases for local test inside source set called test folder. [ no phone /emulator required]
Exception or Improvement Case | Using Robo-electric and supported junit4 dependency, then AndroidJUnit4.class can be used inside test folder to test local test.
Use this dependency[try to add latest]
testImplementation "org.robolectric:robolectric:4.4"
Then in your test class, use #RunWith(AndroidJUnit4::class)
Example : #RunWith(AndroidJUnit4::class)
-------For Reference-----
#RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
#Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh TasksViewModel
var tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
}
}
Looks like you forgot to annotate your TimeExtensionsUnitTest with #RunWith(AndroidJUnit4::class) re: https://github.com/android/android-test/issues/409. I can reproduce the error by commenting that out in my own tests.
That said, this is not the only way to get your error. I originally encountered it when I was updating some old Robolectric tests to use some AndroidX apis (http://robolectric.org/androidx_test/), including switching away from #RunWith(RobolectricTestRunner::class). In my case, I was trying to use a context in my #BeforeClass with ApplicationProvider.getApplicationContext<Context>(), which is wrong in AndroidX, even though it was ok using Roboletric's runner with appContext = RuntimeEnvironment.getApplication().applicationContextappContext. Fortunately I was able to move the logic into my #Before call to fix things without a significant hit.
More generally, I've noticed documentation out there isn't fully comprehensive all in one place explaining the migration from the very old android.support.test Android Testing Support Library and Roboletric dependencies to AndroidX, but here's some other notes:
If I want to run real instrumentation tests on an emulator/physical device, ensure that the tests are under src/androidTest. This also means I need to change in my build.gradle file the dependencies on androidx.test libraries from testImplementation => androidTestImplementation, and also explicitly set a testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" in the defaultConfig section.
https://developer.android.com/training/testing/local-tests
https://developer.android.com/training/testing/instrumented-tests
If I'm happy with using Robolectric to run those tests as local, I can keep them under src/test, declare the dependencies using testImplementation, and don't need the testInstrumentationRunner. It's nice that I don't have to change the code to do this since I'm using the AndroidX Test APIs, which Roboletric is compatible with as of v4.0.
I got the same problem and here is how i fixed it:
move my test class to the "androidTest" package
annotate the test class with #RunWith(AndroidJUnit4.class)
Hope this can help
Simply check your import section:
import androidx.test.runner.AndroidJUnit4;
I am automating accessibility testing with espresso, but it reports that androidx.test.espresso.contrib.AccessibilityChecks as deprecated.
I surfed the internet and found the non-deprecated AccessibilityChecks class which should be used instead. But would be appreciate if anyone can provide the non-deprecated gradle dependency
This is my testing class
import androidx.test.espresso.contrib.AccessibilityChecks;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
#RunWith(AndroidJUnit4.class)
public class AccessibilityChecksTest {
#BeforeClass
public static void turnOnAccessibility() {
AccessibilityChecks.enable();
}
}
Dependency
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0'
The below is the right dependency for the non-deprecated AccessibilityChecks I found it here along with many other Espresso testing APIs
androidTestImplementation 'androidx.test.espresso:espresso-accessibility:3.2.0'
I'm trying to run a local unit test that depends on the context, and was following this guide: https://developer.android.com/training/testing/unit-testing/local-unit-tests#kotlin and I set up my project like this (following this link : https://developer.android.com/training/testing/set-up-project ):
build.gradle(app)
android {
compileSdkVersion 28
buildToolsVersion '27.0.3'
defaultConfig {
minSdkVersion 21
targetSdkVersion 27
versionCode 76
versionName "2.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
useLibrary 'android.test.runner'
useLibrary 'android.test.base'
useLibrary 'android.test.mock'
}
testOptions {
unitTests.returnDefaultValues = true
unitTests.all {
// All the usual Gradle options.
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen { false }
showStandardStreams = true
}
}
unitTests.includeAndroidResources = true
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion", {
exclude group: 'com.android.support', module: 'support-annotations'
})
// Espresso UI Testing dependencies
implementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
testImplementation 'androidx.test:core:1.0.0'
// AndroidJUnitRunner and JUnit Rules
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'
// Espresso Assertions
androidTestImplementation 'androidx.test.ext:junit:1.0.0'
androidTestImplementation 'androidx.test.ext:truth:1.0.0'
androidTestImplementation 'com.google.truth:truth:0.42'
implementation 'androidx.multidex:multidex:2.0.0'
}
My espresso_version is espressoVersion = '3.1.0'
My test that is located in module-name/src/test/java/ looks like this:
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.instacart.library.truetime.TrueTime
import edu.mira.aula.shared.extensions.android.trueDateNow
import edu.mira.aula.shared.network.ConnectivityHelper
import kotlinx.coroutines.experimental.runBlocking
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.util.*
import java.util.concurrent.CountDownLatch
class TimeExtensionsUnitTest {
private lateinit var instrumentationCtx: Context
#Before
fun setup() {
instrumentationCtx = ApplicationProvider.getApplicationContext<Context>()
}
#Test
fun testTrueTimeValueReturnsIfInitialized() {
if (ConnectivityHelper.isOnline(instrumentationCtx)) {
runBlocking {
val countDownLatch = CountDownLatch(1)
TrueTime.build()
.withSharedPreferencesCache(instrumentationCtx)
.withConnectionTimeout(10000)
.initialize()
countDownLatch.countDown()
try {
countDownLatch.await()
val dateFromTrueTime = trueDateNow()
val normalDate = Date()
Assert.assertNotEquals(dateFromTrueTime, normalDate)
} catch (e: InterruptedException) {
}
}
}
}
Everytime I run it, it gives me:
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)
If I run it as a Instrumental Test(changing the package) it runs without errors.
But I thought that this guide was exactly to be able to run unit test using Android Framework classes such as Context.
I even tried run that class UnitTestSample but the same error occurs.
I also removed all android.support dependencies from my project
Any ideas on how to solve it?
Update
You should no longer encounter this error if youre using the latest gradle version.
I also encountered this issue.
If you look at migrating to Robolectric 4.0 here, it suggest to add the following line in your gradle.properties.
android.enableUnitTestBinaryResources=true
The problem is that, if you add this you your gradle.properties, it will output this warning:
WARNING: The option setting
'android.enableUnitTestBinaryResources=true' is experimental and
unsupported.
Now, if you look at Robolectric releases here. You could see that this is a known issue where they state that
Android Gradle Plugin may report the following warning, which may be safely ignored: WARNING: The option setting 'android.enableUnitTestBinaryResources=true' is experimental and unsupported.. Android Gradle Plugin 3.4 will resolve this issue.
I believe unless you could update you gradle to 3.4. You won't be able to solve this issue.
What I did instead was to include Robolectric 4.0 as dependency.
testImplementation "org.robolectric:robolectric:4.0.2"
and annotate my test class with
#RunWith(RobolectricTestRunner::class)
This should make your test work.
Now when you run the test, you'll notice that Robolectric will log the following:
[Robolectric] NOTICE: legacy resources mode is deprecated; see
http://robolectric.org/migrating/#migrating-to-40
Ignore this for now but as soon as you could update your gradle, migrate to the new Robolectric testing.
I follow the official guide also met this issue, fix it with below steps.
Add testImplementation in app build.gradle
// Required -- JUnit 4 framework
testImplementation 'junit:junit:4.13.1'
testImplementation 'androidx.test:core-ktx:1.3.0'
testImplementation 'androidx.test.ext:junit-ktx:1.1.2'
// Robolectric environment
testImplementation 'org.robolectric:robolectric:4.4'
// Optional -- truth
testImplementation 'androidx.test.ext:truth:1.3.0'
testImplementation 'com.google.truth:truth:1.0'
// Optional -- Mockito framework
testImplementation 'org.mockito:mockito-core:3.3.3'
The official guide missed two testImplementations
testImplementation 'androidx.test.ext:junit-ktx:1.1.2'
testImplementation 'org.robolectric:robolectric:4.4'
Add testOptions block in app build.gradle
android {
// ...
testOptions {
unitTests.includeAndroidResources = true
}
}
Add #RunWith(AndroidJUnit4::class) to your test class
Example:
import android.content.Context
import android.os.Build.VERSION_CODES.Q
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
#RunWith(AndroidJUnit4::class)
#Config(sdk = [Q])
class UnitTestWithContextDemoTest {
private val context: Context = ApplicationProvider.getApplicationContext()
fun test_getPackageName() {
assertThat(context.packageName).contains("your_package_name")
}
}
NOTE
#Config(sdk = [Q]) is required when your targetSdkVersion greater than 29. Because robolectric NOT support targetSdkVersion greater than 29.
Spend hours on similar issue, and the problem wasn't in dependencies, rather in AndroidStudio itself Based on the answer:
IDE tries to run local unit tests instead of instrumented
.
Make sure it's run as instrumented test (red is local tests, green - instrumented):
After added instrumented test for the class it's run as expected under instrumented. How I done this? 2 ways I found:
1) Edit configuration (as on the last screenshot) and adding function manually
2) Under Project tap (top left corner) I selected Tests instead of android, found the test, right click - create test. After this step all new tests are run under instrumented tests
I had similar error and was struggling a lot to fix it. My problem was that I was mixing AndroidJUnit4, InstrumentationRegistry, ApplicationProvider and AndroidJUnitRunnerversions / packages. Make sure they all are of the same generation. These are the classes that made it all run for me:
androidx.test.runner.AndroidJUnitRunner
androidx.test.platform.app.InstrumentationRegistry
androidx.test.ext.junit.runners.AndroidJUnit4
androidx.test.core.app.ApplicationProvider
for these I needed the following in the dependencies part of my build.gradle
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation "com.android.support:support-annotations:27.1.1"
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
And of course the correct
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
in my defaultConfig of the build.gradle
Next things are not mentioned on google test guide, but they are what I found:
androidx.test in case of unit tests is just an interface/api (I don't know what about instrumented tests) and it needs implementations, which is robolectric library.
That's why robolectric dependency is also required:
testImplementation "org.robolectric:robolectric:{version}"
#RunWith(AndroidJUnit4.class)
is required. To get nondeprecated class you need to add:
testImplementation "androidx.test.ext:junit:{version}". By the way this dependency has transitive junit4 dependensy.
Also you can faced with: Failed to create a Robolectric sandbox: Android SDK 29 requires Java 9 (have Java 8) in case you use java 8 and compileSdkVersion 29 or above. Here you can find how to deal with it.
Make sure to put your instrumentation tests (tests you run with a Runner) in androidTest and not just test
in my case I was using getApplicationContext() in a fragment from the test package, I just change it to getContext(). and this fixed my crash!
In my case, changing the test method name fixed the issue.
Most probably the Android Studio cached the methods with indexes that caused a problem.
[Note : To remember this ]
AndroidJUnit4.class | used to run testcases of intrumentation type inside source set called androidTest [which requires phone or emulator]
AndroidJUnit4ClassRunner | used to run testcases for local test inside source set called test folder. [ no phone /emulator required]
Exception or Improvement Case | Using Robo-electric and supported junit4 dependency, then AndroidJUnit4.class can be used inside test folder to test local test.
Use this dependency[try to add latest]
testImplementation "org.robolectric:robolectric:4.4"
Then in your test class, use #RunWith(AndroidJUnit4::class)
Example : #RunWith(AndroidJUnit4::class)
-------For Reference-----
#RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
#Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh TasksViewModel
var tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
}
}
Looks like you forgot to annotate your TimeExtensionsUnitTest with #RunWith(AndroidJUnit4::class) re: https://github.com/android/android-test/issues/409. I can reproduce the error by commenting that out in my own tests.
That said, this is not the only way to get your error. I originally encountered it when I was updating some old Robolectric tests to use some AndroidX apis (http://robolectric.org/androidx_test/), including switching away from #RunWith(RobolectricTestRunner::class). In my case, I was trying to use a context in my #BeforeClass with ApplicationProvider.getApplicationContext<Context>(), which is wrong in AndroidX, even though it was ok using Roboletric's runner with appContext = RuntimeEnvironment.getApplication().applicationContextappContext. Fortunately I was able to move the logic into my #Before call to fix things without a significant hit.
More generally, I've noticed documentation out there isn't fully comprehensive all in one place explaining the migration from the very old android.support.test Android Testing Support Library and Roboletric dependencies to AndroidX, but here's some other notes:
If I want to run real instrumentation tests on an emulator/physical device, ensure that the tests are under src/androidTest. This also means I need to change in my build.gradle file the dependencies on androidx.test libraries from testImplementation => androidTestImplementation, and also explicitly set a testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" in the defaultConfig section.
https://developer.android.com/training/testing/local-tests
https://developer.android.com/training/testing/instrumented-tests
If I'm happy with using Robolectric to run those tests as local, I can keep them under src/test, declare the dependencies using testImplementation, and don't need the testInstrumentationRunner. It's nice that I don't have to change the code to do this since I'm using the AndroidX Test APIs, which Roboletric is compatible with as of v4.0.
I got the same problem and here is how i fixed it:
move my test class to the "androidTest" package
annotate the test class with #RunWith(AndroidJUnit4.class)
Hope this can help
Simply check your import section:
import androidx.test.runner.AndroidJUnit4;
I have read Room Persistence Library. I also clone android-architecture-components then I try to add Mirgration test. However, I cannot import
import android.arch.persistence.room.testing.MigrationTestHelper;
I also use latest lib version which is.
android.arch.core:core-testing:1.0.0-alpha3
Here is the code for MigrationTest
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import android.arch.persistence.room.testing.MigrationTestHelper;
#RunWith(AndroidJUnit4.class)
public class MigrationTest {
private static final String TEST_DB = "migration-test";
#Rule
public MigrationTestHelper helper;
public MigrationTest() {
helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
MigrationDb.class.getCanonicalName(),
new FrameworkSQLiteOpenHelperFactory());
}
#Test
public void migrate1To2() throws IOException {
SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
// db has schema version 1. insert some data using SQL queries.
// You cannot use DAO classes because they expect the latest schema.
//db.execSQL(...);
// Prepare for the next version.
db.close();
// Re-open the database with version 2 and provide
// MIGRATION_1_2 as the migration process.
db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2);
// MigrationTestHelper automatically verifies the schema changes,
// but you need to validate that the data was migrated properly.
}
}
Since the code use AndroidJUnit4 then Just use androidTestCompile instead
androidTestCompile "android.arch.persistence.room:testing:$arch_version"
Official doc use dependency for local unit test. However, the official sample use Android runner...
https://developer.android.com/topic/libraries/architecture/adding-components.html
You are using Android runner (AndroidJUnit4.class), and your test is actualy placed at src/androidTest. It means you are using Instrumented Tests which dependencies should be declared:
// Instrumented Unit Test or UI Test
androidTestComplile ....
Meanwhile, if you are writing Local Unit Test, testing codes is placed at src/test, you can declare the dependencies:
// Local Unit Test
testCompile ....
In Google documentation, they just give an example for local unit tests. No mistake here.
The main issue is google provide documentation for JUnit local tests cases and not instrumental (which means tests on a real device or emulator).
As a result i saw many example with
testImplementation "androidx.room:room-testing:$room_version"
This makes not sense cause you can only test your room db with instrumental test cases. You will get an error otherwise. So you have to use androidTestImplementation and not testImplementation in your gradle file.
def room_version = "2.2.5"
def test_version = "1.2.0"
androidTestImplementation "androidx.test:runner:$test_version"
androidTestImplementation "androidx.test:rules:$test_version"
androidTestImplementation "androidx.room:room-testing:$room_version"
The answers of Quang and UmAnusorn are correct but a little outdated. For those who like to know:
Use androidTestImplementation instead of androidTestCompile
AndroidX: androidx.room:room-testing
Complete implementation in gradle:
androidTestImplementation "androidx.room:room-testing:$room_version"
(2.2.4 is the latest version at the moment of writing)