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
More Content by Tyler Schultz