Reputation: 806
I have a Send Feedback in my app's Preferences. It should open a Chooser and a new Intent, to send an email with provided email address, subject and text template. This part simply works both on emulator and physical device:
prefButtonSendFeedback?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
val emailIntent = Intent(Intent.ACTION_SENDTO)
emailIntent.type = "text/plain"
emailIntent.data = Uri.parse("mailto:")
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(getString(R.string.send_feedback_email_address)))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.send_feedback_email_subject))
emailIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.send_feedback_email_content_template))
context?.startActivity(Intent.createChooser(emailIntent, getString(R.string.send_feedback_intent_name)))
true
}
I have also Espresso test for this case:
@Test fun clickOnFeedbackActionMenuItem_StartsFeedbackFragment() {
launchFragmentInContainer<SettingsFragment>()
Intents.init()
intending(
hasAction(Intent.ACTION_CHOOSER)
).respondWith(
ActivityResult(Activity.RESULT_OK, null)
)
// Press on Feedback navigation item
onView(withId(androidx.preference.R.id.recycler_view))
.perform(
actionOnItem<RecyclerView.ViewHolder>(
hasDescendant(withText(R.string.pref_send_feedback)), click()
)
)
val emailIntent = allOf(
hasAction(Intent.ACTION_SENDTO),
hasData(Uri.parse("mailto:")),
hasExtra(Intent.EXTRA_EMAIL, arrayOf(withText(R.string.send_feedback_email_address))),
hasExtra(Intent.EXTRA_SUBJECT, withText(R.string.send_feedback_email_subject)),
hasExtra(Intent.EXTRA_TEXT, withText(R.string.send_feedback_email_content_template))
)
val expectedIntent = allOf(
hasAction(Intent.ACTION_CHOOSER),
hasExtra(equalTo(Intent.EXTRA_INTENT), emailIntent),
hasExtra(equalTo(Intent.EXTRA_TITLE), withText(R.string.send_feedback_intent_name))
)
intended(expectedIntent)
Intents.release()
}
My issue is similar in general to one from How to stub Intent.createChooser Intent using Espresso
androidx.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: Wanted to match 1 intents. Actually matched 0 intents.
IntentMatcher: (has action: is "android.intent.action.CHOOSER" and has extras: has bundle with: key: "android.intent.extra.INTENT" value: (has action: is "android.intent.action.SENDTO" and has data: is <mailto:> and has extras: has bundle with: key: is "android.intent.extra.EMAIL" value: is [<with string from resource id: <3134850762>>] and has extras: has bundle with: key: is "android.intent.extra.SUBJECT" value: is <with string from resource id: <2131820702>> and has extras: has bundle with: key: is "android.intent.extra.TEXT" value: is <with string from resource id: <2131820701>>))
Matched intents:[]
Recorded intents:
-Intent { act=android.intent.action.CHOOSER (has extras) } handling packages:[[android]], extras:[Bundle[{android.intent.extra.INTENT=Intent { act=android.intent.action.SENDTO dat=mailto: (has extras) }, android.intent.extra.TITLE=Send Feedback:}]])
val emailIntent = allOf(
hasAction(Intent.ACTION_SENDTO),
hasData(Uri.parse("mailto:")),
)
val expectedIntent = allOf(
hasAction(Intent.ACTION_CHOOSER),
hasExtra(equalTo(Intent.EXTRA_INTENT), emailIntent),
)
val receivedIntent: Intent = Iterables.getOnlyElement(Intents.getIntents())
assertThat(receivedIntent).hasAction(Intent.ACTION_CHOOSER) ### here is a breakpoint
It shows me, that my Intent has all Extras I need (subject, address and text) - I removed unnecessary values:
receivedIntent =
(...)
mAction = "android.intent.action.CHOOSER"
(..)
mExtras =
mMap =
value[0] =
mAction = "android.intent.action.SENDTO"
mData = "mailto:"
mExtras =
mMap =
value[0] = "App Feedback"
value[1] = "Add here any bugs, issues or ideas about App"
value[2] =
0 = "[email protected]"
value[1] = "Send Feedback:"
My question is, what is wrong with my Matchers - for me they look ok:
Moreover, I am not sure how intending()
works - I assume that using it like in my code will not run real Intent during test. So it will not run an email client. I am wrong - it runs, but without it all next test hangs even, if I am using Intents.release()
.
Upvotes: 3
Views: 1354
Reputation: 10175
I still don't understand the difference, so if anyone can explain it to me...
withText
is a Matcher<View>
which is for matching TextViews - it's specifically looking for a TextView
that has the specified string resource ID. Unless you're shoving a TextView with the same resource ID into your extras, it ain't going to work.
Upvotes: 0
Reputation: 1
hasExtra(key: String, value: T)
-> hasEntry(is(key), is(value))hasExtra(keyMatcher, valueMatcher)
-> BundleMatcher(keyMatcher, valueMatcher)hasExtras(matcher: Mathcer)
-> new TypeSafeMatcher
-> matcher.matches(intent.getExtras())
I use like this simply.
intended(FilterMatcher.filter<Intent> { intent ->
//check with intent
})
class FilterMatcher<T> (klass: Class<T>, val filter: (data: T) -> Boolean): TypeSafeMatcher<T>(klass) {
companion object {
inline fun <reified T> filter(noinline filter: (data: T) -> Boolean): Matcher<T> = FilterMatcher(T::class.java, filter) as Matcher<T>
inline fun <reified T: View> filterView(noinline filter: (data: T) -> Boolean): Matcher<View> = FilterMatcher(T::class.java, filter) as Matcher<View>
}
override fun describeTo(description: Description?) {
description?.appendText("with filter");
}
override fun matchesSafely(item: T): Boolean = filter(item)
}
Upvotes: 0
Reputation: 806
So, once I dig deeper in web resources, I found more complex Espresso matchers:
val expectedIntent = allOf(
hasAction(Intent.ACTION_CHOOSER),
hasExtra(
equalTo(Intent.EXTRA_INTENT),
allOf(
hasAction(Intent.ACTION_SENDTO),
hasData(Uri.parse("mailto:")),
hasExtra(
`is`(Intent.EXTRA_TEXT),
`is`("Add here any bugs, issues or ideas about App")
),
hasExtra(
`is`(Intent.EXTRA_EMAIL),
`is`(arrayOf("[email protected]"))
),
hasExtra(
`is`(Intent.EXTRA_SUBJECT),
`is`("App Feedback")
)
)
),
hasExtra(
`is`(Intent.EXTRA_TITLE),
`is`("Send Feedback:")
)
)
intended(expectedIntent)
It simply works.
I still don't understand the difference, so if anyone can explain it to me...
I don't know, why this one works:
hasExtra(
`is`(Intent.EXTRA_TITLE),
`is`("Send Feedback:")
)
and this one not:
hasExtra(
`is`(Intent.EXTRA_TITLE),
withText(R.string.send_feedback_intent_name)
)
Upvotes: 3