Is there a way to test Chrome Custom Tabs with Espresso? - android

Here is the stub of the code.
Click data item on ListView . Works as designed and opens Chrome Custom Tab :
onData(anything()).inAdapterView(withId(R.id.listView))
.atPosition(0).perform(click());
Pause(5000);
Espresso.pressBack();
Cannot seem to evaluate anything in the tab or even hit device back button.
Getting this error
Error : android.support.test.espresso.NoActivityResumedException: No
activities in stage RESUMED.
Did you forget to launch the activity. (test.getActivity() or similar)?

You can use UIAutomator (https://developer.android.com/training/testing/ui-automator.html). You can actually use both Espresso and UIAutomator at the same time. See the accepted answer on the following post for more information:
How to access elements on external website using Espresso

You can prevent opening Custom Tabs and then just assert whether the intent you are launching is correct:
fun stubWebView(uri: String) {
Intents.intending(allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(uri)))
.respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null))
}
fun isNavigatedToWebView(uri: String) {
Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(uri)))
}
This way you can avoid Espresso.pressBack() in your test.
Note that since these are using Espresso Intents, you need to either use IntentsTestRule or wrap these with Intents.init and release like this
fun intents(func: () -> Unit) {
Intents.init()
try {
func()
} finally {
Intents.release()
}
}
intents {
stubWebView(uri = "https://www.example.com")
doSomethingSuchAsClickingAButton()
isNavigatedToWebView(uri = "https://www.example.com")
}

A suggestion for improved readability following Mustafa answer:
intents{
Intents.intended(
CoreMatchers.allOf(
IntentMatchers.hasAction(Intent.ACTION_VIEW),
IntentMatchers.hasPackage("com.android.chrome")
)
)
}

Related

How to restart Android app within Espresso test?

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)

How do i relaunch application from start for every individual test case in Espresso Android

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.

How to check that popup notification is NOT displayed. Espresso

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))))
}

How to start activity in espresso test with airbnb DeepLinkDispatch

I got to a project which uses Airbnb DeepLinkDispatch library. This part works fine, I'm able to run activities via URI + query params which parse fine too.
But when I tried using Espresso I got this issue - Intent does not contain Extra with URI and params.
The test I wrote:
class Test {
companion object {
#ClassRule
#JvmField
val rule = ActivityTestRule<MyActivity>(MyActivity::class.java, true, false)
}
#Before
fun setUp() {
val intent: Intent = MyActivity.createIntent(false)
rule.launchActivity(intent)
}
#Test
fun firstTest() {
onView(withId(R.id.switch))
.check(matches(isDisplayed()))
.perform(click())
Screengrab.screenshot("testtest")
}
}
I found out that onCreate in DeepLinkActivity is not called (the class which is annotated with #DeepLinkHandler(AppModule::class).
One way how to fix this could be adding missing EXTRA to the custom intent in setUp(), but this is something I don't want to do. It's a fragile solution and prone to future issues.
Any other ideas how to get espresso + deeplink running together?

Android Espresso - Web browser

I have a question
I want to test whether after button click the web browser is beign launched using espresso.
And the question is: is it even possible to test such thing?
If so any ideas how would I do that?
Although it's an old question but just posting here to help anyone else. I had the same situation where i wanted to verify whether a particular url was launched in browser or not. I got real help from this link
I got it working using this chunk of code:
Intents.init();
Matcher<Intent> expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(EXPECTED_URL));
intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
onView(withId(R.id.someid)).perform(click());
intended(expectedIntent);
Intents.release();
So, it tests when browser is opened with correct url and intending() does the magic here by enabling intent stubbing. Using this, we can intercept it so the intent is never sent to the system.
For convenience I suggest a full example:
Production code:
register_terms.text = Html.fromHtml(getString(R.string.register_terms,
getString(R.string.privacy_policy_url),
getString(R.string.register_terms_privacy_policy),
getString(R.string.general_terms_and_conditions_url),
getString(R.string.register_terms_general_terms_and_conditions)))
Strings XML:
<string name="register_terms">By registering you accept our <a href=\"%1$s\">%2$s</a> and the <a href=\"%3$s\">%4$s</a>.</string>
<string name="register_terms_privacy_policy">Privacy Policy</string>
<string name="register_terms_general_terms_and_conditions">General Terms and Conditions</string>
<string name="privacy_policy_url" translatable="false">https://www.privacypolicy.com</string>
<string name="general_terms_and_conditions_url" translatable="false">https://www.generraltermsandconditions.com</string>
Test code:
#Before
fun setUp() {
Intents.init()
}
#After
fun tearDown() {
Intents.release()
}
#Test
fun when_clickPrivacyLink_then_openPrivacyUrl() {
val expected = allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(string(privacy_policy_url)))
Intents.intending(expected).respondWith(Instrumentation.ActivityResult(0, null))
onView(ViewMatchers.withId(R.id.register_terms))
.perform(openLinkWithText(string(register_terms_privacy_policy)))
Intents.intended(expected)
}
Actually no. Espresso will allow you to click in the button and once the browser is fired up, the test will end. The alternative you have is having your class that fires the browser Intent mocked, so that you can test the remaining flow (if any).
HAve a look in this answer: Controlling when an automation test ends - Espresso, where I describe how you could achieve that.

Categories

Resources