I have a few tests that only seem to fail the first time i run them, here is an example of one of them;
class OptionDialogTest {
#get:Rule
val activityScenarioRule = activityScenarioRule<MainActivity>()
#Test
fun optionDialogTest() {
val selected = R.string.manage_categories
var dialog: OptionDialog? = null
activityScenarioRule.scenario.onActivity { activity: MainActivity? ->
dialog = OptionDialog("test", "test", arrayOf(R.string.drawer_categories,selected), activity as Context)
dialog!!.setConfirm { _, _ -> }
dialog!!.show()
}
onView(withId(R.id.custom)).perform(click()) //this line sometimes doesn't execute causing the test to fail, presumably it doesn't execute in time.
onView(withText(R.string.manage_categories)).inRoot(isPlatformPopup()).perform(click())
onView(withText(R.string.Yes)).inRoot(isDialog()).perform(click())
assertEquals(dialog!!.getSelected(), selected)
}
}
the error android studio gives is; Waited for the root of the view hierarchy to have window focus and not request layout for 10 seconds. If you specified a non default root matcher, it may be picking a root that never takes focus.
my only guess is that some of the code isn't executing in an completely asyncronous fashion. I don't know if other developers experiance random test failures, or how i would go about fixing this.
any help or ideas are appreciated.
Related
created a simple test class to check the statistics of a TO-DO List app.
refer - https://learn.udacity.com/courses/ud940/lessons/d6e700ab-b5d9-4b7b-8bab-64ab79c88f87/concepts/e4e37d5c-2b18-403d-83d9-1646949396f4
the IDE shows no tasks available even though tasks have been added!
new to testing please help me out.
class StatisticsUtilsTest{
#Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero(){
// Create an active tasks (the false makes this active)
val tasks= listOf<Task>(
Task("title","description", isCompleted = false)
)
// Call our function
val result= getActiveAndCompletedStats(tasks)
// Check the result
assertEquals(0f,result.completedTasksPercent)
assertEquals(100f,result.activeTasksPercent)
}
reference error image
I am trying to test my lazy column in JetpackCompose and I keep getting this error:
[Compose-Espresso link] to become idle timed out
I tried using composeTestRule.waitforIdle() but it doesn't work. What am I missing here?
#HiltAndroidTest
class MainTest {
#get:Rule(order = 1)
var hiltTestRule = HiltAndroidRule(this)
#get:Rule(order = 2)
var composeTestRule = createAndroidComposeRule<MainActivity>()
private val context = InstrumentationRegistry.getInstrumentation().context
#Before
fun setup() {
hiltTestRule.inject()
composeTestRule.setContent {
HomePage(
context = context,
viewModel = composeTestRule.activity.viewModels<MarvelViewModel>().value,
onClick = {}
)
}
composeTestRule.onRoot().printToLog("currentLabelExists")
}
#Test
fun isResultDisplayedOnLazyColumn() {
composeTestRule.waitForIdle()
composeTestRule.onNode(hasImeAction(ImeAction.Done)).performTextInput("iron man")
composeTestRule.onNode(hasImeAction(ImeAction.Done)).performImeAction()
composeTestRule.onNodeWithTag(TAG_LAZY_COLUMN, useUnmergedTree = true).assertIsDisplayed()
I've got this issue too. The root cause was having my composeview GONE. It seems ComposeTestRule IdlingResource don't like then you call setContent on a Gone ComposeView
After several hours of trials and experimentation, l found a way to fix this issue in a recent project. I would advise to try the following:
Update compose version to 1.2.0-Alpha06 as Skav mentioned. For me that implies bumping my kotlin version to 1.7.0 which my team is not ready for.
So I did some digging around the IdlingRegistry and found that unregistering Espresso-Compose Idling resource at the right spot in the test fixed the issue and my UI test passed afterwards.
IdlingRegistry.getInstance().resources.forEach {
Timber.e("resource ${it.name} idleNow: ${it.isIdleNow}")
if (it.name == "Compose-Espresso link") {
IdlingRegistry.getInstance().unregister(it)
}
}
It is important to note that unregistering Espresso-Compose too early can make your compose ui to fail to display. In my case, l ran this code after the UI is ready before testing for specific UI functionalities.
Building on top of the answer from #leeCoder, I've discovered that upgrading to Compose 1.2.0 (alpha no longer necessary) fixed my problem and there was no need to unregister anything (which was the part that made me the most nervous about his answer).
I am using Espresso with Kotlin for the UI test automation. I am trying to find a proper way to restart the app during the test and start it again, so the test scenario is the following:
start the app, go to login page
force close the app and open it again (basically restart it)
check some stuff etc
The way our UI tests are organized:
there is a test class where I have rules
val intent = Intent(ApplicationProvider.getApplicationContext(), MainActivity::class.java)
.putExtra(UI_TEST_INTENT, true)
#get:Rule
val rule = ActivityScenarioRule<MainActivity>(intent)
there Before/After functions and tests functions in this class
What I want is to have generic restartApp function in separated class, let's say TestUtils and to be able to call it at any point of time, when is needed.
So far I didn't find a solution. There are some similar questions on stackoverflow, but I am not sure I understand how to work with the answers I found, like this:
with(activityRule) {
finishActivity()
launchActivity(null)
}
Since ActivityTestRule is deprecated and documentation asking to use ActivityScenarioRule, I tried this:
#get:Rule
val rule = ActivityScenarioRule<MainActivity>(intent)
private fun restart() {
rule.scenario.close()
rule.scenario.recreate()
}
but it gets java.lang.NullPointerException
another option is
private fun restart() {
pressBackUnconditionally()
Intents.release()
ActivityScenario.launch<MainActivity>(intent)
}
it works, app restarts but I can not interact with the app anymore, because for some reason there are two intents running now
Would be great to get an answer I can work with (I am quite new to Espresso)
Cheers
The solution is found:
private fun restart() {
Intents.release()
rule.scenario.close()
Intents.init()
ActivityScenario.launch<MainActivity>(intent)
}
Seems like the author's answer has some excess code. The following is enough
activityScenarioRule.scenario.close()
ActivityScenario.launch(YourActivity::class.java, null)
I have a testsuite which has mutiple testcases in a class
every test case is isolated
So when i execute the testsuite class i want to restart the app for every testcase
How do i relaunch application from start for every individual test case in Espresso
Thanks in advance
#Test
public void testcase1() {
//from first screen
}
#Test
public void testcase2() {
//from first screen
}
There is another stack overflow answer that seems to answer this question. If you were looking to do that in Kotlin though I converted the answer to relaunch multiple times for different tests.
#RunWith(AndroidJUnit4::class)
class ExampleEspressoTest {
#get:Rule
val rule = ActivityTestRule(
activityClass = MainActivity::class.java,
initialTouchMode = false,
launchActivity = false) //set to false to customize intent
#Test
fun testCustomIntent() {
val intent = Intent().apply {
putExtra("your_key", "your_value")
}
rule.launchActivity(intent)
//continue with your test
}
}
If you need to start a method/test and when it's finished clear data and start the next one, you should use commands.
Look at this documentation: https://developer.android.com/studio/test/command-line
I'm using this command:
./gradlew testVariantNameUnitTest --tests *.sampleTestMethod
There could be several ways to do this but we wanted a way that works both locally as well as google fire base test lab, so ended up with using configuration in build.gradle file under default config.
defaultConfig{
testInstrumentationRunnerArguments clearPackageData: 'true'
}
Reference: https://developer.android.com/training/testing/junit-runner#ato-gradle
Also you use these runner arguments for configuring different tests you wanted run based on build variants or other config options, look at my post if you want more detail.
I have popup notification. My first test checks if that notification is displayed and it passes succesfully. I use next method for this test:
fun viewInNotificationPopupIsDisplayed(viewMatcher: Matcher<View>) {
onView(viewMatcher)
.inRoot(RootMatchers.isPlatformPopup())
.check(ViewAssertions.matches(isDisplayed()))
}
I have a problem with second test case where i have to check that my popup notification has already gone (means it's not displayed anymore).
So i'm trying to use next method:
fun viewInNotificationPopupIsNotDisplayed(viewMatcher: Matcher<View>) {
Espresso.onView(viewMatcher)
.inRoot(RootMatchers.isPlatformPopup())
.check(matches(not(isDisplayed())))
//.check(ViewAssertions.doesNotExist()) // doesn't work as well
}
I get next exception:
android.support.test.espresso.NoMatchingRootException:
Matcher 'with decor view of type PopupWindow$PopupViewContainer'
did not match any of the following roots:
[Root{application-window-token=android.view.ViewRootImpl$W#bb8371e,
window-token=android.view.ViewRootImpl$W#bb8371e, has-window-focus=true,
Please, can anybody help with this?
Seems that such simple check is impossible with espresso if you have a PopupWindow because of the way the NoMatchingRootException is thrown
You may just catch the exception and complete the test but that's not a good option since throwing and catching NoMatchingRootException consumes a lot of time in a comparison with the default test case. Seems that Espresso is waiting for the Root for a while
For this case is suggest just to give up with espresso here and use UiAutomator for this assertion
val device: UiDevice
get() = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun assertPopupIsNotDisplayed() {
device.waitForIdle()
assertFalse(device.hasObject(By.text(yourText))))
}
fun assertPopupIsDisplayed() {
device.waitForIdle()
assertTrue(device.hasObject(By.text(yourText))))
}