I am trying to write tests for the PreferenceFragments fragment in Settings.
However, I've been getting this error:
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: is assignable from class: class android.widget.AdapterView
The code for the Test is the following:
#RunWith(AndroidJUnit4.class)
#SmallTest
public class SettingsFragmentTest {
#Rule
public ActivityTestRule<SettingsActivity> mActivityRule = new ActivityTestRule<>(
SettingsActivity.class);
#Test
public void preferredLocationShouldBeVisibleOnDisplay(){
mActivityRule.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
SettingsFragment settingsFragment = startSettingsFragment();
}
});
// This check passes correctly
onView(withId(R.id.weather_settings_fragment))
.check(matches(isCompletelyDisplayed()));
// This check gives me the NoMatchingViewException
onData(allOf(is(instanceOf(Preference.class)),
withKey("location")))
.check(matches(isCompletelyDisplayed()));
}
private SettingsFragment startSettingsFragment(){
SettingsActivity activity = mActivityRule.getActivity();
FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
SettingsFragment settingsFragment = new SettingsFragment();
transaction.replace(R.id.weather_settings_fragment, settingsFragment, "settingsFragment");
transaction.commit();
return settingsFragment;
}
}
The settings_activity layout looks as follows:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.example.android.sunshine.SettingsFragment"
android:id="#+id/weather_settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
And the Preferences Screen layout is the following:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditTextPreference
android:defaultValue="#string/pref_location_default"
android:inputType="text"
android:key="#string/pref_location_key"
android:singleLine="true"
android:title="#string/pref_location_label" />
<ListPreference
android:defaultValue="#string/pref_units_metric"
android:entries="#array/pref_units_options"
android:entryValues="#array/pref_units_values"
android:key="#string/pref_units_key"
android:title="#string/pref_units_label" />
<CheckBoxPreference
android:defaultValue="#bool/show_notifications_by_default"
android:key="#string/pref_enable_notifications_key"
android:summaryOff="#string/pref_enable_notifications_false"
android:summaryOn="#string/pref_enable_notifications_true"
android:title="#string/pref_enable_notifications_label" />
</PreferenceScreen>
I haven't been able to find any examples or information online on how to test PreferenceFragments. Most of the information related to testing Activities.
PreferenceMatchers seems to work only with the preference classes from the Android framework, but not with support preference library (com.android.support:preference-v14). Since the latter uses a RecylerView internally, I was able to get hold of the preference items by using RecyclerViewActions from espresso-contrib:
onView(withId(R.id.list))
.perform(RecyclerViewActions.actionOnItem(hasDescendant(withText(R.string.pref_manage_categories_title)),
click()));
For PreferenceFragment:
PreferenceFragment uses a ListView internally, so you can use onData() with allOf() and withTitle():
onData(allOf(is(
instanceOf(Preference.class)),
withTitle(R.string.my_pref_string)))
.perform(click());
Or use onData() with allOf() and withKey():
onData(allOf(is(instanceOf(Preference.class)), withKey("myPrefKey")))
.onChildView(withText(R.string.my_pref_string))
.perform(click());
Or use onData() with anything(), but this can be unreliable and cause timeouts:
// atPosition() is required - Not sure why
onData(anything())
.atPosition(3)
.onChildView(withText(R.string.my_pref_string))
.perform(click());
Asserting or checking for a preference item is done in a similar way:
onData(allOf(is(
instanceOf(Preference.class)),
withTitle(R.string.my_pref_string)))
.check(matches(isDisplayed()));
For PreferenceFragmentCompat:
PreferenceFragmentCompat uses a RecyclerView internally, so you must use the espresso-contrib library, as mentioned in the answer by mtotschnig on 3 Septemebr 2018. If you're using the androidx.preference:preference:1.x.x library with unit tests written in Kotlin, it can be tricky to know what to use. You can start with something like this:
onView(withId(androidx.preference.R.id.recycler_view))
.perform(actionOnItem<RecyclerView.ViewHolder>(
hasDescendant(withText(R.string.my_pref_title)), click()))
Note: To quickly add the imports for these methods, put the blinking cursor on the unresolved method, then do Android Studio ➔ Help ➔ Find Action ➔ search for "show context action" or "show intention action" ➔ click on the result option ➔ A popup window will appear ➔ click on "Import static method ...". You can also assign a keyboard shortcut to "Show Context Actions". More info here. Another way is to enable "Add unambiguous imports on the fly" in the Settings.
After navigating to your settings activity, you can use Espresso withText() with the preference android:title String
#Rule
public ActivityTestRule<SettingsActivity> mActivityRule = new ActivityTestRule<>(
SettingsActivity.class);
onView(withText(mActivityRule.getActivity().getResources().getString(R.string.my_pref_title))
.perform(click());
Side note: if it is a ListPreference, then when clicking on it as mentioned above, then you can test selecting an item from the list the same way, but use the list entry text in withText() as no titles here.
onView(withText(mActivityRule.getActivity().getResources().getString(R.string.list_entry_sring))
.perform(click());
I normally test Fragments through their activity with Espresso. Just test the UI exposed by your Fragment as if it were in the Activity that you're starting with the ActivityTestRule. If the Fragment isn't present on initial launch of the Activity, navigate in your test the same way a user would in order to start the Fragment transaction. If you find yourself needing to test business logic that requires you to stand up Fragments in some sort of test harness, that's usually a good indicator that it should be pulled out into a separate class that can be (ideally) unit tested devoid of any Android dependencies.
You can always try Record Espresso test
https://developer.android.com/studio/test/espresso-test-recorder.html
Here is the tips on how to work with lists
https://developer.android.com/training/testing/espresso/lists.html
This is PreferenceMatcher that expose API like this:
onData(PreferenceMatchers.withKey(mContext.getString(R.string.key_settings)))
.perform(click());
About PreferenceFragmentCompat, in addition to #Mr-IDE answer,
checking for a preference item with scrollTo<RecyclerView.ViewHolder>
onView(withId(androidx.preference.R.id.recycler_view))
.perform(
scrollTo<RecyclerView.ViewHolder>(
hasDescendant(withText(R.string.my_pref_title))
)
)
#Test
public void clickListPreference() throws Exception{
// Check if it is displayed
Context appContext = InstrumentationRegistry.getTargetContext();
onData(allOf(
is(instanceOf(Preference.class)),
withKey(appContext.getResources().getString(R.string.pref_units_key))))
.check(matches(isDisplayed()));
// Check if click is working
onData(allOf(
is(instanceOf(Preference.class)),
withKey(appContext.getResources().getString(R.string.pref_units_key))))
.onChildView(withText(appContext.getResources()
.getString(R.string.pref_units_label))).perform(click());
}
Hope this will help you..
Related
I'm working on a little app with a lot of modifiable preferences, most of them being SeekBarPreferences.
It happens that, since I'm quite not happy with Android default SeekBarPreferences, I'm using the very good MaterialSeekBarPreference library which unfortunately have not been updated for 2 years.
Here is an example of code used by this library:
<com.pavelsikun.seekbarpreference.SeekBarPreference
android:key="#string/param_maxEvent"
android:title="Blahlblahblahblahblah"
android:summary="Blahlblahblahblahblah too"
android:defaultValue="2"
custom:msbp_minValue="0"
custom:msbp_maxValue="5"
custom:msbp_measurementUnit="events"
custom:msbp_interval="1"
custom:msbp_dialogEnabled="false"/>
As you can see, you can use the android:defaultValue xml attribute, and it works perfectly with the UI.
Since I need to load all these default values at app initialization, I use the PreferenceManager.setDefaultValues method:
public class App extends Application {
#Override public void onCreate() {
super.onCreate();
PreferenceManager.setDefaultValues(this, R.xml.preferences, true);
}
}
This works fine with all default preferences (SwitchPreference, ListPreference, Preference), but unfortunately not with these custom SeekBarPreference.
Loading the preferences activity does not set up thoses default values either.
Is there any workaround for this problem ? Else, if I was up to edit the library, what should I change ?
I don't use the method PreferenceManager.getDefaultSharedPreferences(this, R.xml.preferences, true);. Instead, I use preference.setDefaultValue(object); in the Fragment.
I am using Appium for automating my Android app. I have a fragment to enter email and after verifying that second fragment with passwordfield will be loaded. I am able to find all elements in the first fragment; but elements in the second fragment can't be found using any of the methods like By, PageFactory + #AndroidFindBy. Can someone provide a help to sort this issue?
Use ExplicitWait method.
public static void ExplicitWait(MobileElement element){
(new WebDriverWait(driver,30)).until(ExpectedConditions
.elementToBeClickable(element));
}
then before you use element of second fragment call ExplictWait
ExplictWait(passwordField);
passwordField.sendKeys("your password");
I am writing UI Automation tests in Espresso for Android & came across a scenario for which I haven't got any solution so far.
In one Fragment, I have OptionsMenu with a single item. The state of that MenuItem is set according to value from API response.
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.clear();
getActivity().getMenuInflater().inflate(R.menu.menu_cancel_order, menu);
MenuItem cancelMenuItem = menu.findItem(R.id.cancel_order);
if(something) { // something can be a boolean value from server
cancelMenuItem.setEnabled(true);
} else {
cancelMenuItem.setEnabled(false);
}
}
For UI testing, I need to write test case for checking whether this MenuItem is enabled/disabled.
For clicking on the overflowmenu,
ViewInteraction actionMenuItemView = onView(
allOf(withId(R.id.action_settings), withContentDescription("Settings"), isDisplayed()));
actionMenuItemView.perform(click());
And so far what I have tried to check the Assertion is given below.
onView(allOf(withText("Cancel Order"), withId(R.id.cancel_order))).check(matches(not(isEnabled())));
But this fires NoMatchingViewException with message
NoMatchingViewException: No views in hierarchy found matching: (with
text: is "Cancel Order" and with id:
com.equinix.ecp.betatest:id/cancel_order)
So I tried changing it to
onView(allOf(withText("Cancel Order"))).check(matches(not(isEnabled())));
Somehow this matched the view but it was not a MenuItem but the TextView inside the MenuItem & since I am setting setEnabled() to MenuItem, check() Assertion won't work as expected since it is a TextView.
So my question is how to write Test for checking enabled/disabled state of MenuItem.
it would be a good idea to make use of uiautomatorviewer, to put a breakpoint in at the point your test fails, and then inspect your app's layout for clues
it sounds to me that you have two views. One with the id of R.id.cancel_order and another with text "Cancel Order" which probably has another id (or could/should).
So together they return NoMatchingView, because they aren't the same view.
They could be sibling views, or possibly one is a descendent of another. This is where uiautomatorviewer is very handy for figuring out what's happening on screen
as long as you've installed "Android SDK Platform-Tools" and "Android SDK Tools"
from Terminal:
cd /Users/<user name>/Library/Android/sdk/tools/bin
./uiautomatorviewer
(it's also helpful to save this as a script and just use an alias shortcut for convenience)
as for your matcher, i would try :
onView(allOf(
withId(R.id.cancel_order),
hasSibling(withText("Cancel Order"))
)).check(matches(not(isEnabled())));
or change hasSibling(_) to hasDescendent(_) or isDescendentOfA(_), depending on their relationship (which you can find out by using uiautomatorviewer)
I would suggest you use the IDs of the menu items to perform your checks.
I tried it with this menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="at.hellobank.hellomarkets.symbols.DetailActivity">
<item
android:id="#+id/action_1"
android:icon="#android:drawable/arrow_down_float"
android:title="Menu1"
app:showAsAction="always" />
<item
android:id="#+id/action_2"
android:enabled="false"
android:icon="#android:drawable/arrow_down_float"
android:title="Menu2"
app:showAsAction="always" />
</menu>
So one menu item is enabled one is disabled. My test to check this looks like this and is working as expected:
#Test
public void testMenuItemsStatus() throws Exception {
onView(withId(R.id.action_1)).check(matches(isEnabled()));
onView(withId(R.id.action_2)).check(matches(not(isEnabled())));
}
Generally using IDs in tests is better imho because you are more independent of typos and general language. withText("Cancel Order") probably will not work if you test the app localized in another language.
How can we use espresso onView and perform in a Settings activity that contains a PreferenceFragment like this:
http://developer.android.com/guide/topics/ui/settings.html#Fragment
The Preferences are in a list so you have to query for the specific preference like this:
// Check if is displayed
onData(allOf(is(instanceOf(Preference.class)), withKey("prefkey"))).check(matches(isDisplayed()));
// Perform click
onData(allOf(is(instanceOf(Preference.class)), withKey("prefkey"))).onChildView(withClassName(is(Switch.class.getName()))).perform(click());
I found this article helpful: http://www.winters.nz/2015/05/espresso-for-android-hints-properties.html
Try this below logic
// Check if it is displayed
onData(PreferenceMatchers.withKey(context.getResources().getString(R.string.prefkey))).check(matches(isDisplayed()));
I have ,in a fragment, a method call who open an AlertDialog when an user tap on an button, in that dialog I would like to show a Spinner with countries ( Spain, Italy, French....)
My code for the spinner is the following:
RestCountries restCountries = new RestCountries();
List<RestCountries.Datum> countries = restCountries.data;
String mCities ="";
ArrayList<String> citiesArrayList = new ArrayList<>();
for(RestCountries.Datum data : countries){
mCities = data.name;
citiesArrayList.add(mCities);
}
ArrayAdapter spinnerAdapter = new ArrayAdapter(getActivity(),android.R.layout.simple_spinner_dropdown_item, citiesArrayList );
mCountrySpinner.setAdapter(spinnerAdapter);
The spinner is showed emphy after the dialog is opened.
On the logcat I get
Could not find class 'android.widget.ThemedSpinnerAdapter', referenced
from method
android.support.v7.widget.AppCompatSpinner$DropDownAdapter.<init>
Any idea about what I am doing wrong
In my case I solved the problem just setting for all the modules in the project the same SDKCompileVersion. Here it is my complete answer in a similar question
Cheers
Could not find class 'android.widget.ThemedSpinnerAdapter' [Android Studio]
I faced and win this problem!
In case if you are using AndriodAnnotations here the problem is that I filled the lists in the method onCreate().
I used to get View via findViewById(R.id...) and worked with them.
Now, as it turned out during debugging, all Views is not created in onCreate() yet!
The problem was solved when I found an annotation #AfterViews in the docs, and the method under this annotation now fills all my actions and does initialization of fields.
So, anyway, check your code on NullPointerException caused by invoking empty view object.
This may not help everyone, but I was having this issue trying to add a spinner to a PopupWindow.
I updated my compileSdkTarget from 23 to 25, and my support library version to 25.1.0, but it didn't help.
It turned out that changing the spinnerMode to "dialog" worked round the problem:
<Spinner
android:id="#+id/group_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
/>
It doesn't completely fix it of course if you really want a dropdown spinner.
There are several different causes of this problem.
In my case (was trying to sign up to Parse), I got this error trying the app on a tablet.When I switched to an Android phone, I got the error message:
You must register this ParseObject subclass before instantiating it
So, In my App.java class I did this:
public class App extends Application {
public void onCreate() {
super.onCreate();
Parse.enableLocalDatastore(this);
Parse.initialize(this, "PARSE APPLICATION ID", "PARSE CLIENT KEY");
}
}
and then in my manifest, I did this:
<application
android:name=".App"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
...
That was it.Had nothing to do with Spinner