Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Android: Unit testing custom views

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.

Comments
  1. jrhamza says:

    Hi Tyler,

    Thanks for sharing this.
    I am facing one issue while doing with customtextview with custom attribute. I have mentioned custom style for the view in the layout xml as follows,

    I cannot inflate layout using your example with this view. Can you please help me on this ?

    Thanks in advance,

    JRH

  2. Brian says:

    For anyone currently referring to this example:
    loadingTextView = (LoadingTextView) LayoutInflater.from(new Activity()).inflate(R.layout.loading_text, null);

    Will throw IllegalStateException

    It has been replaced with:
    Activity activity = Robolectric.buildActivity(Activity.class).create().get();
    loadingTextView = (LoadingTextView) LayoutInflater.from(activity).inflate(R.layout.loading_text, null);

    I recommend downloading the RobolectricSample project as Tyler suggests.

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *