Android: Unit testing custom views

June 11, 2011 Tyler Schultz

Android Activity classes can become gigantic and unwieldy if you’re not careful. Testing complex Activities requires complex setup. To reduce that pain we try our best to keep the Activity lean. This means getting logic out of the Activity. One technique we use is to create custom views that we can test in isolation.

Here is an example of a view class that starts out with a ProgressBar spinner, and when the text is set, the spinner is hidden, and the text becomes visible:

public class LoadingTextView extends RelativeLayout {
    ...
    public void stopLoadingAndSetText(int text_res_id) {
        TextView textView = (TextView) findViewById(loading_text_text_view);
        textView.setText(text_res_id);
        textView.setVisibility(VISIBLE);

        findViewById(loading_text_spinner).setVisibility(View.GONE);
    }
}

Here is the layout xml that is associated with the custom view class. Notice the reference to the LoadingTextView class in the outermost tag (which has lost its formatting!!!):

<?xml version="1.0" encoding="utf-8"?>

<com.pivotallabs.views.loadingtextview android:layout_height="wrap_content" android:layout_width="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">

    <linearlayout android:gravity="center" android:padding="10dip" android:orientation="horizontal" android:layout_height="wrap_content" android:id="@+id/loading_text_spinner" android:layout_width="fill_parent">
        <progressbar android:layout_height="wrap_content" android:layout_width="wrap_content" />
        <textview android:paddingLeft="10dip" android:text="Loading..." android:layout_height="wrap_content" android:layout_width="wrap_content" />
    </linearlayout>

    <textview android:visibility="invisible" android:layout_height="wrap_content" android:id="@+id/loading_text_text_view" android:layout_width="fill_parent" />

</com.pivotallabs.views.loadingtextview>

Robolectric will allow you to instantiate the Android subclasses in the JVM, but the problem is that newing the custom view class will not inflate the elements the view class expects to be present. To get around this you can take advantage of Robolectric’s simulated view inflating functionality. Use the LayoutInflator.from() method to get an inflator which can be used to inflate your view:

@RunWith(RobolectricTestRunner.class)
public class LoadingTextViewTest {
    private LoadingTextView loadingTextView;
    private View loadingSpinner;
    private TextView loadingTextTextView;

    @Before
    public void setUp() throws Exception {
        loadingTextView = (LoadingTextView) LayoutInflater.from(new Activity()).inflate(R.layout.loading_text, null);
        loadingSpinner = loadingTextView.findViewById(R.id.loading_text_spinner);
        loadingTextTextView = (TextView) loadingTextView.findViewById(R.id.loading_text_text_view);
    }

    @Test
    public void testStopLoadingAndSetTextShouldHideTheSpinnerAndShowTheTextView() throws Exception {
        assertThat(loadingSpinner.getVisibility(), equalTo(View.VISIBLE));
        assertThat(loadingTextTextView.getVisibility(), equalTo(View.INVISIBLE));

        loadingTextView.stopLoadingAndSetText(R.string.unit_tests_ftw);

        assertThat(loadingSpinner.getVisibility(), equalTo(View.GONE));
        assertThat(loadingTextTextView.getVisibility(), equalTo(View.VISIBLE));
    }

    @Test
    public void testStopLoadingAndSEtTextShouldSetTheTextOnTheTextView() {
        loadingTextView.stopLoadingAndSetText(R.string.unit_tests_ftw);

        assertThat((String) loadingTextTextView.getText(), equalTo("Unit Tests FTW!!!"));
    }
}

You can run these example tests yourself by checking out the RobolectricSample project on github.

About the Author

Biography

More Content by Tyler Schultz
Previous
What's Your Start-up's "Bus Count"?
What's Your Start-up's "Bus Count"?

Tim Ferriss (author of "The 4-Hour Workweek" and lots more) recently posted an interview with Pivotal's CEO...

Next
Making WebMock, Selenium, and WebDriver Play Nicely
Making WebMock, Selenium, and WebDriver Play Nicely

Update Since this article was written, version 1.7.0 of WebMock has been released, which includes WebMock....