Open davenotdavid opened 5 years ago
Hi @DaveNOTDavid, still running into problems here? I have a working setup to test the authorization flow using UiAutomator (no Espresso), so it probably seems to be an issue with your test config...
@tobi512 could you please share the working test setup?
Hey @ishaan-khan not sure if that code still exists (and more important: still works). It was 3 years ago, I'll check in the coming days and let you know when I still find it...
Hi @tobi512 thanks for responding. Were you able to get hold of the code? I'm quite hopeful that it will help me with this issue.
Hey @ishaan-khan sorry was pretty busy these days, but I found the code and I'll leave it here for you. It's not used in production anymore, so can't guarantee that it still works and can't give further support here, good luck. 😄 (I remember that I somehow took it from a tutorial I found somewhere on the web with slight modifications).
LoginUiTest.kt
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class LoginUiTest {
private lateinit var device: UiDevice
/**
* Uses package manager to find the package name of the device launcher. Usually this package
* is "com.android.launcher" but can be different at times. This is a generic solution which
* works on all platforms.`
*/
private val launcherPackageName: String
get() {
// Create launcher Intent
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_HOME)
// Use PackageManager to get the launcher package name
val pm = getApplicationContext<Context>().packageManager
val resolveInfo = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
return resolveInfo!!.activityInfo.packageName
}
@Before
fun startMainActivityFromHomeScreen() {
// Initialize UiDevice instance
device = UiDevice.getInstance(getInstrumentation())
// Start from the home screen
device.pressHome()
// Wait for launcher
val launcherPackage = launcherPackageName
assertNotNull(launcherPackage)
device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT_MILLIS.toLong())
// Launch the app
val context = getApplicationContext<Context>()
val intent = context.packageManager
.getLaunchIntentForPackage(EXAMPLE_APP_PACKAGE)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) // Clear out any previous instances
context.startActivity(intent)
// Wait for the app to appear
device.wait(
Until.hasObject(By.pkg(EXAMPLE_APP_PACKAGE).depth(0)),
TIMEOUT_MILLIS,
)
}
@Test
fun testSuccessfulLogin() {
assertNotNull(device) // If this assert fails, something with the setup is wrong
// navigate to the login button on settings tab
val settingsTab = By.res(EXAMPLE_APP_PACKAGE, "settings")
device.wait(Until.findObject(settingsTab), TIMEOUT_MILLIS * 2)
device.findObject(settingsTab).click()
device.findObject(By.res(EXAMPLE_APP_PACKAGE, "login_button")).click()
// The first run experience of chrome will show up at this point. Click through the pages since overriding chrome settings doesn't work.
device.findObject(UiSelector().resourceId("com.android.chrome:id/terms_accept")).click()
device.findObject(UiSelector().resourceId("com.android.chrome:id/negative_button")).click()
// All elements on the login webview are best accessible via UiSelector
// (use the "uiautomatorviewer" from <Android-SDK>/tools/bin to check their resourceIds)
device.wait(Until.findObject(By.text("Login")), TIMEOUT_MILLIS * 2)
device.findObject(UiSelector().resourceId("emailAddress-input")).text = EMAIL
device.findObject(UiSelector().resourceId("password-input")).text = PASSWORD
// this is used as a waiting time, the resource doesn't exist on purpose!
device.wait(Until.findObject(By.text("Unknown")), TIMEOUT_MILLIS)
device.findObject(UiSelector().text("Login")).click()
device.wait(Until.findObject(By.res(EXAMPLE_APP_PACKAGE, "customer_id")), TIMEOUT_MILLIS * 2)
val customerId = device.findObject(By.res(EXAMPLE_APP_PACKAGE, "customer_account_id"))
assertEquals(customerId.text, SOME_CUSTOMER_ID)
}
companion object {
private val EXAMPLE_APP_PACKAGE = "com.example.app"
private val TIMEOUT_MILLIS = 5000L
private val EMAIL = "test@example.com"
private val PASSWORD = "passw0rd"
private val SOME_CUSTOMER_ID = "Customer id: 123456"
}
}
Following dependencies were added for it in the build.gradle:
androidTestImplementation 'androidx.test.ext:junit:1.1.2-alpha02'
androidTestImplementation 'androidx.test:runner:1.3.0-alpha02'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
androidTestImplementation 'org.hamcrest:hamcrest-integration:1.3'
Current environment including, but not limited to:
With the following code snippets, I've been trying to use Espresso (
UiAutomator
for automatically filling in the user input fields) for testing out the login auth flow where logging in occurs externally via a custom Chrome Tab intent with a successful login taking the user back to the app via the launching Activity'sonActivityResult()
callback to then run some logic afterwards (asserting that the screen really changed by validating that the next screen's views are being displayed in this case). But it turns out that the app isn't resumed properly after logging in which later throws aNoActivityResumedException
.And yes, I've tried using Espresso Intents, but couldn't figure out how to tie it in this scenario since I'm going as far as testing the overall login flow within the login screen as the
ActivityTestRule
, particularly triggering its own intent (auth request) after the login button is pressed. I feel like I'm on the right track so far, so any help would be appreciated on pointing me to the right direction!Login screen:
Helper auth functions:
Espresso UI test:
... but
LoginActivity
is not resumed as shown here in the logs (prior to aNoActivityResumedException
):