Robust React Native element finding using Appium - android

I'm using React Native to develop an app that requires end-to-end testing.
Coming from a Selenium WebDriver background, it seems to me that element finding in Appium can be very awkward compared to WebDriver, since native components only have ID and a single non-user-defined class.
Is there a way I can add some sort of metadata to elements to make them easy to find?
Say if I have a table with complex elements inside a cell, and I need to first find the right row, then the cell, and then find the right components to manipulate inside the cell.
Ideally I'd want to be able to enumerate rows by searching for something like "myTargetRows". That would give me a list of rows.
If I understand it correctly, no two components can have the same ID in native applications, so I can't use ID just like that, right? (I.e. use the same ID, "myTargetRows", for multiple row components).
If not, should I use numbered IDs like "myTargetRow0", "myTargetRow1", etc, and then use XPath to partially match the ID?
Isn't there a better way?
If I can do something like that, can I then just chain those findElement calls to find the right nested elements I want?
P.s.: I don't want to hardcode the exact component hierarchies in my locators to avoid invalidating them should I move anything around in my views, so those point-and-click solutions won't help.
P.s. 2: Solutions must work both on Android and iOS, even if I need to implement some sort of abstraction for it myself.

You should use accessibilityLabel props of View.
accessibilityLabel PropTypes.node
Overrides the text that's read by the screen reader when the user
interacts with the element. By default, the label is constructed by
traversing all the children and accumulating all the Text nodes
separated by space.
More info can be found here
For unique ids, lets say you have a listview and 100 rows. You can combine rowId and static text for accessibilityLabel.
eg: 0_MyCustomRow, 1_MyCustomRow

Since appium supports different search strategies which are also slightly different per platform I am using the following approach in one of my projects:
I have a helper which can be easily imported into each class which returns cross platform (iOS and Android) test ID props
export function getTestIdProps (id) {
return { accessible: true, accessibilityLabel: id, testID: id }
}
During Development we make sure that each to be tested component receives those test IDs similar to this:
return (
<ComponentToBeTested>
<View {...getTestIdProps('unique-test-id-goes-here')} moreProps>
</View>
</ComponentToBeTested>
)
Careful Make sure to check the react native docs to see which basic element supports theses props. Not all react native elements support the testID or accessibilityLabel. If they don't, these props will be ignored.
Finally, in your Appium Test (Example in Java) you can then identify the element easily by its accessibility ID, similar to this:
public static By byAccessibilityId(final String id) {
return MobileBy.AccessibilityId(id);
}
For further reading also see http://appium.io/docs/en/commands/element/find-elements/

Take a look at article in Sauce Labs blog, thats a good a example of dealing with RN apps.

Related

Android Data-Binding : Example vs Reality

In all the data-binding examples that shows Generic data type handling developer.android.com uses real char < and >.
but when it comes to reality
I am getting below error.
The value of attribute "type" associated with an element type
"variable" must not contain the '<' character.
I've searched the web and found people use > for > and < for < as a fix.
Questions
Is this supposed to happen ? If yes why it's not mentioned in the docs ?
Is there any fix for this, where I can write the layout as given in the official docs? (without using corresponding html entity characters)
There's unlikely to be a change to this because layout files are still XML, this isn't really the fault of Android or DataBinding, you're going to need to use appropriate encoding for HTML entities within an XML document.
Using < isn't that terrible as a fix, as far as resolutions go, but if you'd rather avoid using it, then it may be an option to simplify your binding expressions to move logic away from the layout and into your variables.
The current advised method of doing so is with a ViewModel, which can be bound to the layout and expose observable LiveData values.
I can't give you a reason for it not being in the documentation besides that it's probably just not advised to do so.
Now they've updated the documentation

Best way to display first doc in a view using the UnpBootFormViewer

I am trying to display the first document in a view using the "UnpBootFormViewer".
What I have currently done is include the "UnpBootFlatView" control that references a view which contains only one (1) document (app settings in this instance). I have it in a "" with the style set to "display: none;".
This works great on a computer using a standard browser, but when the same app is opened on a mobile device (Nexus 5) it does nto work.
Is there a better/easier way to do what I'm trying?
I assume you use native document data source. To initialize it with first document from some view you must provide UNID of that document. Example:
var v:View = database.getView("view name");
var vec:ViewEntryCollection = v.getAllEntries();
var ve:ViewEntry = vec.getFirstEntry();
var unid = ve.getUniversalID();
return unid;
I recommend to use your preferred error catching technique (try/catch, OpenLog...)
Why so complicated - ViewEntryCollection and ViewNavigator collections keep documents sorted as in view. Common beginner's mistake is to use view.getFirstDocument() - this document is the first from internal list of documents so it need not to be the first document shown in the view.
I think what you are trying to do is possible and I think it's demostrated in this page from the sample app provided by Teamstudio
My Activities Page
This uses the UnpBootFlatView to look up the activities based you your username. It then uses the UnpBootFormViewer to display the first document in the view.
The XPage to reference is MyActivities
The documentations also points out that from release 1.3 of the XControls 'openfirstdocument' is defaulted to 'yes'. If you have this set to anything else the first document in the view will not load.
Flat View Documentation
If that's all fine on the standard browser as you suggest and it just doesn't work on the Android device, then I'd suggest contacting Teamstudio Support.
Tim C. ;o)
[Disclosure] Tim Clark works for Teamstudio

Android hierarchy for XPath to interact with specific item

I'm new to automatization, Android, Selenium, Appium and xpath, too. I know it's suck a great beggining.
I write tests for Android devices, but the application I have to test have a lot of costum views. I found out the best way to interact with these custom items is to put an "android:contentDescription" field in the Views. My only question is how to get access to the element with have a specified contentDescription? This is az android specific question, I'm not even sure that the content-desc is the field I'm looking for.
I have the hierarchy provided by Android UI Animator Viewer:
http://i.imgur.com/NUGc56o.png
The ways i've tried:
xpath: //*[contains(#android:contentDescription,'example text')]
I was able to get access by finding them as an ImageView, but as I mentioned I need to work with custom Views
My code looks like somtihng like this:
driver.findElementByXPath("//*[constains(#content-desc,'Login')]").click();
Thanks for the help!
You could also try using Accessibility labels or the UIAutomator locator strategy.
Here's Appium's documentation on those.
Your xpath is incorrect. It should be: "//android.widget.ImageView[#content-desc='Login']"
Here's some pseudocode of what you should do:
login_image = driver.findElementByXPath("//android.widget.ImageView[#content-desc='Login']"); // Gets you the WebElement
print login_image.getClass(); // Just for debugging, make sure it's not nil/null
login_image.click(); // Click on it!

Why Table Views (UITableView) have to have standalone source object that provide them data?

I wonder why all Table Views (also in Android) have to have special object that provide them data ?
https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instp/UITableView/dataSource
Why Table Views can't be populated from controllers (or Activity class in the case of Android) ?
For example UILabel in iOS can be populated just by setting "text" property
https://developer.apple.com/library/ios/documentation/uikit/reference/UILabel_Class/Reference/UILabel.html#//apple_ref/occ/instp/UILabel/text
Could you explain what is the purpose of such design ?
This is a really great question.
We can break this into two reasons - a general one and a specific one that is a common example of it.
First, though, notice that it's even more than that. A UITableView can have two delegate controller objects, a UITableViewDataSourceDelegate and a UITableViewDelegate. The first provides the data; the second responds to actions taken on the displayed data. Why all these layers of abstraction? Why not just stick it in the UIViewController?
The general reason not to is suggested by the name 'UIViewController'. It's meant to control views. In iOS and I expect in android-land, there is a strong temptation to just 'stick it in the view controller' - leading to the Very Large View Controller antipattern, which you can read about a lot (eg here).
Far better for maintenance and organization for the view controller to lay out views and handle button presses, and some other controller object to handle the well-defined and really quite separate responsibility of providing the data.
It's not uncommon to have some sort of object that handles the data anyway - a shopping cart, or list of real estate properties, etc - that encapsulates additional business logic (providing subtotals, for example) but fundamentally already 'has' the data. In that case, provide a category or a lightweight controller that can 'bridge' between that data and the table view, and pass the model object, instead of an array, between view controllers.
This gets into the specific: in iOS, we have things like NSFetchedResultsController. This is a fantastic class that does a tremendous amount of data sorting, pagination, addition, deletion, slicing, dicing, folding, spindling, and mutilating for you - and it also speaks UITableView's language natively. Pass one of those around and you keep your view controllers clean and tidy and separate from your data.
TableViews do need a special or separate object but they do require an object implement the correct protocol so that it can find the data. In Apple's default implementation, it Uses a UTableViewCotnroller that is a new object responds to these methods as well as does some other things such as making the UITableView the root view of the view controller.
If you do not want to use that object for whatever reason you can still manually add a UITableView to your view controller, then sets its delegate to the view controller. Although it is a good to move it out of the view controller if possible to help keep the view controller small and the data reusable.

Android Spinner - System view VS User view

I have been creating Spinner controls (Combo boxes/Drop downs) in one of my apps, and was surprised to find out how difficult it was to achieve all of the following features:
User facing Strings are externalized, taking advantage of strings.xml internationalisation (I18N) feature of Android.
Spinner selections operate using a System view, which facilitates not having to work with or map Strings to meaningful values (yuck).
User view to System view mapping should be easy, automated and minimal (i.e not hand rolled for every component).
Others have attempted solutions to this, but universally as far as I could see they suffer from one or many of the following problems:
UI code is creeping into their enum class which doesn’t belong there (messy), nearly all existing solutions suffered from this.
Hardcoded User facing Strings in their enum classes. Because these are not externalized you cannot do I18N using the stock Android features.
Authors typically make the Fragment or Activity an OnItemSelectedListener which perpetuates a common problem of inheritance for convenience, where composition is more appropriate.
I have developed my own solution which does this: http://www.androidanalyse.com/android-spinner-externalize-user-strings-mapped-to-system-enum/
My question is, have I missed something? This seems like something that should not have been this hard (which makes me feel like I'm possibly reinventing the wheel).
Below is some example code showing my solution in-use (which is available Apache 2 license from the link above).
String none = getString(R.string.none);
String light = getString(R.string.light);
String medium = getString(R.string.medium);
String strong = getString(R.string.strong);
SpinnerUtil.createNewSpinner(view, R.id.wind, Arrays.asList(none, light, medium, strong), WindLevel.values(),
new SpinnerItemSelectedListener<WindLevel>() {
public void onItemSelected(Spinner item, WindLevel value) {
// Take whatever action you wish to here.
}});
I would just use ArrayAdapter<WindLevel>. Yes, you created a custom typed listener, but the regular event listener gets the position and can call getItem() on the ArrayAdapter<WindLevel> to get a WindLevel properly typed.
IMHO, the vast majority of Spinner widgets will be populated with material read in from a database, the Internet, or some other dynamic data source, rather than populated by some sort of enum with display values coming from static strings that can be internationalized ahead of time.
This is not to say that your code is useless: if you find it useful, then it was worth writing. And I am sure that there are apps out there that contain your targeted pattern (i.e., a Spinner backed by an enum or equivalent where the display values are known in advance and can be internationalized) who might find your solution useful as well. Every developer who writes enough code cooks up these sorts of helper classes and the like that help map an OS or framework model into something that better fits the developer's own mental model. So long as you are not perceiving any performance issues, it's all good.
Also, note that OnItemSelectedListener is an interface; implementing that interface on an existing class is not inheritance.
I believe the reason nobody answered you is :
What problem are you trying to solve ? Spinners existed prior to your well designed attempt.
Why reinvent them in exactly the same way they exist in Android ?
http://developer.android.com/guide/topics/ui/controls/spinner.html
It is a beautiful wheel indeed you designed, but still, it is just a wheel :)
UPDATE :
I think I begin to understand what you did. This is interesting. I'm not sure why you did not go to the pattern implemented by the ListPreference with its entries and entryvalues.
In fact, I'm not sure I understand why the Android team did not go that route either.
In any case, it is worth proposing your idea to the Android framework. It is after all open source.

Categories

Resources