Friday, May 25, 2012

Best practices for unit testing Android apps


I'd like to unit test my Android application but I found that test driven development in Android is far from trivial at the moment.



Any tips, tricks, war stories for building light weight and preferably fast running tests?


Source: Tips4all

17 comments:

  1. You should try Robotium! Go to Robotium.org and download the example test project. Robotium is a really easy to use framework that makes testing of android applications easy and fast. I created it to make testing of advanced android applications possible with minimum effort. Its used in conjunction with ActivityInstrumentationTestCase2.

    ReplyDelete
  2. Also worth checking out is Robolectric, an alternative approach to testing Android apps.

    Instead of deploying your app and tests to the Android emulator (which can be very slow), Robolectric runs your tests directly in your computer's JVM, reducing typical test turnaround times from minutes to seconds.

    Robolectric allows you to test most Android functionality including layouts and GUI behavior, services, and networking code. It offers much greater flexibility than Google's testing framework in testing some Android features, such as widgets and services.

    ReplyDelete
  3. Looking at ApiDemos sample app, i found ActivityUnitTestCase and ActivityInstrumentationTestCase classes.

    Seems that these are utility classes for testing android programs.

    Here are the links to the reference:
    http://developer.android.com/reference/android/test/ActivityInstrumentationTestCase.html
    and
    http://developer.android.com/reference/android/test/ActivityUnitTestCase.html

    Also android.jar includes a subset of JUnit test framework for plain old unit test. Take a look at ApiDemos sample for learning how to write and run it.

    Hope this help!

    ReplyDelete
  4. I've been working with Android Unit and Functional tests using the Instrumentation Framework. Its documentation is now clearer than in the past. These links can guide you to start testing:


    Introduction to the framework and all related classes:
    http://developer.android.com/guide/topics/testing/testing_android.html
    A code sample of these classes:
    http://developer.android.com/resources/tutorials/testing/helloandroid_test.html
    A sample of how to test Activities:
    http://developer.android.com/resources/tutorials/testing/activity_test.html

    ReplyDelete
  5. Working in android has helped me keep the separation of concerns in order. Keep as much logic out of the view as possible. Follow a common UI design pattern like MVC or MVP. Then that model logic can be unit tested with straight jUnit. I have three projects setup in eclipse.


    One is the Android applications project.
    The second is the test project where the view-dependent tests are run by adb
    The third is where the standard JUnit tests are located.


    This doesn't change the fact that using adb shell to run the Android tests is cumbersome. All I've been able to do is minimize the number of cumbersome tests.

    As far as war stories: I was happy to figure out round trip testing the custom Parcelable.

    ReplyDelete
  6. Three projects aren't necessary (per James answer above). You can get POJO junit 4 tests to run in the Android test project without having the emulator running or device connected. I think the best practice is two projects - one for source, one for tests, and within tests, expect to have tests that are POJO (no android references/emulation required) and tests that require emulation.

    But a catch (and a fix)... When I used the Eclipse Android Plugin to "Create a New Android Test Project", Eclipse wouldn't run the junit test with the junit test runner, it will only run them with the Android test runner on the emulator or attached device. Even after I created a new JUnit 4 test and eclipse added Junit 4 jar to the project.

    The fix: Go to Run > Run Configurations... Select your Junit test case run config - the one that failed. To the left of Apply it will probably say "Using Eclipse JUnit Test Launcher - Select other...". Even though this seems right, it's not. Click Select other... and choose Android JUnit Test Launcher. Click Run and it should work. If it doesn't, right click on your test case and chose Run As... > JUnit Test. Thanks to Dan Syrstad for the tip.

    I believe the initial run config should be Android JUnit Test, but that when you run you can select the JUnit Test config to not require emulation.

    And be sure to connect your device for Android unit tests - its WAY faster than the emulator - no start-up time. Very easy to configure - see instructions here:

    http://developer.android.com/guide/developing/device.html

    ReplyDelete
  7. For final integration testing, Robotium is the way to go. However, for detailed coverage and very fast unit testing natively on the development PC (instead of the simulator or on real hardware,) I use PowerMock to mock the Android objects.

    I use this in conjunction with Infinitest to enable automatic testing every time I save a file so I can get immediate feedback about whether I have broken anything.

    Here's a sample of what my PowerMock unit tests look like:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({ TextView.class, EditText.class, Editable.class, Toast.class})

    public class NameWasEnteredTest {
    @Mock Context context;
    @Mock Editable editable;

    EditText firstName;
    EditText lastName;
    Button cancelButton;
    Toast toast;

    @Test
    public void simpleTest() {
    NameWasEntered.
    nameWasEntered(firstName, lastName, cancelButton, context);
    verifyThatFirstAndLastNameEditTextsAreCleared();
    verifyThatCancelButtonTextChangedToDone();
    verifyThatToastWasShown();
    }

    private void verifyThatCancelButtonTextChangedToDone() {
    verify(cancelButton).setText("Done");
    }

    private void verifyThatToastWasShown() {
    PowerMockito.verifyStatic();
    Toast.makeText(eq(context), eq("Name was entered"), anyInt());
    }

    private void verifyThatFirstAndLastNameEditTextsAreCleared() {
    verify(firstName).setText(null);
    verify(lastName).setText(null);
    }

    @Before
    public void setUp() {
    MockitoAnnotations.initMocks(this);
    firstName = PowerMockito.mock(EditText.class);
    lastName = PowerMockito.mock(EditText.class);
    cancelButton = PowerMockito.mock(Button.class);
    toast = PowerMockito.mock(Toast.class);
    PowerMockito.mockStatic(Toast.class);
    when(Toast.makeText(eq(context), anyString(), anyInt())).
    thenReturn(toast);
    when(firstName.getText()).thenReturn(editable);
    when(lastName.getText()).thenReturn(editable);
    when(editable.toString()).thenReturn("string");
    }
    }


    Related question.

    ReplyDelete
  8. Aside from easily testing non platform dependent logic I haven't found a clever way to run tests, so far (at least for me) any actual platform logic testing is cumbersome. It's almost non trivial anyway because I've found differences in implementation between the emulator and my actual device and I hate to run a unit test implementation on my device just to remove the application afterwards.

    My strategy has been: Try to be concise and make the logic well thought out and then test implementation piece by piece (less then desirable).

    ReplyDelete
  9. I recently released Borachio, a native Scala mocking framework which works on Android.

    Because Borachio is written in Scala, you’ll need to write your tests in Scala. But it can be used to test code written in Java.

    There's a description of how to use Borachio on Android on my blog:

    http://www.paulbutcher.com/2011/03/mock-objects-on-android-with-borachio-part-1/
    http://www.paulbutcher.com/2011/03/mock-objects-on-android-with-borachio-part-2/
    http://www.paulbutcher.com/2011/03/mock-objects-on-android-with-borachio-part-3/

    ReplyDelete
  10. I found this presentation over at Slideshare to be helpful (http://www.slideshare.net/dtmilano/testing-on-android). This combined with the blog post over at 8th Light (http://blog.8thlight.com/articles/2009/7/12/up-and-running-with-tdd-on-android) and looking at the unit test examples, scarce as they may be, in the API Demos app helped me to get started doing TDD on my Android app.

    ReplyDelete
  11. Although, this is the old question.

    Just to keep all answers togeher:
    Here Stephen Ng provides good aproach for real Unit Test for Android projects solution: https://sites.google.com/site/androiddevtesting/

    ReplyDelete
  12. I am fairly new to testing and found Hello, Testing very helpful.

    ReplyDelete
  13. Anyone tried the Mockito Framework in conjunction with android development? I always wanted to try it out. It is similar to EasyMock but doesn't depend on interfaces. It seems to create descending classes from your classes under test transparently. The only limitation I'm aware of is testing of final classes and function does not work...

    ReplyDelete
  14. I recommend using EasyMock extensively for unit testing. I recommend it highly -- the only problem is that somehow on Android (because of some Dalvik VM issues), it can only mock interface classes, and throws errors otherwise. We work around this by creating a TestableClass for every Class that we have, and making that class implement a mock interface that we can test against. I can describe more if people are interested.

    ReplyDelete
  15. Here's a ScreenCast I made on how I got Unit Tests to work. Simple Unit Tests and more complex unit tests that depend on having a reference to Context or Activity objects.

    http://www.gubatron.com/blog/2010/05/02/how-to-do-unit-testing-on-android-with-eclipse/

    ReplyDelete
  16. You can also have a look at PowerMock which extends EasyMock and Mockito and allows you to mock e.g. final classes and static methods as well as accessing internal state etc.

    ReplyDelete
  17. The trick explained above by user279199 works to run POJO JUnit tests within an Android project. However, NOT to debug POJO JUnit tests within an Android project. I think debugging still requires the third project as mentioned by James A Wilson.

    If anyone has found a way to debug POJO JUnit tests without starting/running on the emulator - please advise HOWTO. :)

    ReplyDelete