Easier Android UI tests with CSS-style selectors
Java
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.

README.md

Android ViewSelector

ViewSelector makes unit testing Android UIs easier with CSS-style selectors. It can express complex assertions about the UI with little code and without manually fussing around with views.

// These traditional assertions ...
ListView view = (ListView) activity.findViewById(R.id.groceries);
assertEquals(2, view.getChildCount());
assertEquals("milk", ((TextView) view.getChildAt(0)).getText());
assertEquals("cereal", ((TextView) view.getChildAt(1)).getText());

// ... can be expressed as:
assertThat(selection("ListView#groceries TextView", activity))
    .attribute("text").containsExactly("milk", "cereal");

It's compatible with both Android tests and Robolectric (1.2 & 2.x) tests. It was inspired by the very handy assert_select in Rails and Android's own UiSelector.

Currently under development.

Installation

Maven

Releases are available from Maven Central. Add this dependency to your pom.xml:

<dependency>
  <groupId>com.nikhaldimann</groupId>
  <artifactId>android-view-selector</artifactId>
  <version>1.0-beta-1</version>
  <scope>test</scope>
</dependency>

JARs

If contrary to all that is good and reasonable you're not using Maven, you can download the lastest JAR with dependencies (android-view-selector-RELEASE-jar-with-dependencies.jar) from Sonatype.

API

Examples

ViewSelector uses fluent assertions (based on FEST and FEST Android):

import static com.nikhaldimann.viewselector.ViewSelectorAssertions.assertThat;
import static com.nikhaldimann.viewselector.ViewSelectorAssertions.selection;
...

// Assert that rootView has 5 descendant views that are TextViews
assertThat(selection("TextView", activity)).hasSize(5);

// Assert that there are 4 TextViews that are descendants of the view with id
// "container" and all are visible with a width of 100 pixels
assertThat(selection("#container ImageView", activity))
    .hasSize(4)
    .isVisible()
    .hasWidth(100);

// Assert that the TextViews which are direct children of a LinearLayout with
// id "groceries" have text "milk", "cereal" (in that order)
assertThat(selection("LinearLayout#groceries > TextView", activity))
    .attribute("text").containsExactly("milk", "cereal");

The second argument to selection() can be an activity or any View.

If you're already statically importing a different FEST-style assertThat method, you can statically import ViewSelector's assertThatSelection to avoid conflicts:

import static com.nikhaldimann.viewselector.ViewSelectorAssertions.assertThatSelection;
...

// Equivalent to the first assertion above
assertThatSelection("TextView", activity).hasSize(5);

You may also consult these two full examples of tests using ViewSelector:

Supported Selectors

Selector semantics mirror those of CSS very closely. These selectors are supported so far:

Selector Example Selects ...
Universal * ... all views
View type TextView ... views of type TextView
View id #foo ... views with id foo
Descendants GridLayout TextView ... TextViews that are descendants of a GridLayout
Children #foo > ImageView ... ImageViews that are direct children of a view with id foo
Union TextView, ImageView ... views of type TextView or ImageView
Attribute existence TextView[tag] ... TextViews that have a tag attribute that isn't null
Attribute equality TextView[tag=foo] ... TextViews that have a tag attribute value equal to the string "foo"
Attribute contains TextView[tag*=foo] ... TextViews that have a tag attribute value containing the substring "foo"
Attribute prefix TextView[tag^=foo] ... TextViews that have a tag attribute value starting with "foo"
Attribute suffix TextView[tag$=foo] ... TextViews that have a tag attribute value ending with "foo"
Adjacent sibling TextView + ImageView ... ImageViews directly preceded by a TextView
General sibling TextView ~ ImageView ... ImageViews preceded by a TextView

Notes on Attribute Matching

The attribute selectors are implemented by calling corresponding getters on the views. For example, the selector [tag] calls getTag() on views to find out whether that attribute exists. Naming conventions for boolean attributes are respected, e.g., the selector [isShown] will call isShown() (not getIsShown()) on views.

Selectors with attribute matching (e.g., [tag=foo]) support some primitive value types other than strings in a natural way:

  • The values true and false when used with a boolean attribute will be interpreted as booleans rather than strings. Example: [isShown=true]
  • Integer values used with integer attributes will be interpreted as integers. However, due to limitations in the CSS parsing library used the numbers have to be enclosed in quotes. Example: [minWidth='100']

Note that attribute matching for some attributes might not behave as you expect if you're using Robolectric because of the shadowing techniques it uses.

License

Distributed under an MIT license.