Android – Basic testing with unit test, android test and roboguice, robolectric, mockito

Bugs are always the most annoyed things for developer. It’s a dream (which never comes true) that a software runs without any problem at client’s site. There are always some points that developers have to improve after deployment. The more time developers invest for testing, the better the software is and the less annoyance developers have. I made many examples of Android development which are small apps and illustrate how a point of interest works. As a demo, I didn’t care too much about if there is a bug in app code because it isn’t a complete application. However if we want to make something that can be sold to customers, we have to be sure that our software should be reliable as it could be. Therefore in this post I would like to discuss some simple basic tests in Android development. We will learn how to write a logical unit test with JUnit, an integration unit test with test classes from Android and some advanced test cases with Roboguice, Robolectric and Mockito.

1. Prerequisites

For logical unit test and Android test cases, you don’t need to know something special but Android programming technique. However for Roboguice which I used a lot in my examples, if you still don’t know what it is then read this post Android, Dependency Injection (IOC) with roboguice and MVVM (Model-View-ViewModel) pattern as your starting point.

I use Intellij IDEA as IDE for developing Android app, if you use Eclipse then try to search in Internet for instructions of adding test classes.

2. Testing application

For illustrating how testing in Android works, I prepare a simple calculator app with 3 activities: a basic mode, an advanced mode and an about activity like images below

Basic mode

Basic Mode

Advanced mode

Advanced mode

About

About

The Basic mode provides 4 simple operators, Advance mode provides 4 advanced operators and About just contains a text about the app. The Advance mode and About can be accessed over menus in ActionBar. For illustrating different test techniques, the Basic mode and About activity are raw Activity of Android. The Advance mode will be built with RoboActivity and be applied with Dependency Injection.

public class MainActivity extends Activity {
    /**
     * Called when the activity is first created.
     */

    private EditText editTextOperand1;
    private EditText editTextOperand2;
    private Button buttonAdd;
    private Button buttonSubtract;
    private Button buttonMultiply;
    private Button buttonDivide;
    private TextView textViewResult;
    MainActivityViewModel viewModel;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        initializeComponent();

	}
	...
}

public class AboutActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.about);

    }
}

The Advance Activity will be built with Dependency Injection of Roboguice.

public class AdvanceActivity extends RoboActivity {

    @InjectView(R.id.editTextOperand1)
    private EditText editTextOperand1;
    @InjectView(R.id.buttonSqrt)
    private Button buttonSqrt;
    @InjectView(R.id.buttonFactorial)
    private Button buttonFactorial;
    @InjectView(R.id.buttonLogarith)
    private Button buttonLogarith;
    @InjectView(R.id.buttonTenPower)
    private Button buttonTenPower;
    @InjectView(R.id.textViewResult)
    private TextView textViewResult;
    @Inject
    IAdvanceActivityViewModel viewModel;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.advance);

        initializeComponent();
    }
	...
}

I won’t dig too much in app code because it’s really simple. You can read the code later yourself. The main concept of Basic mode and Advanced mode is using a ViewModel (the business logic layer) for calculating result. The only difference between them is abstraction. In Basic mode I use a concretely defined ViewModel class, meanwhile in Advance mode I use an interface as abstraction so that I can inject something else later. That is a good point so that you can realize the big advantage of Dependency Injection.

So we have now an app to test. Back to our tests, according to performance of testing, as usual, I categorize my test cases into 3 levels: Basic, Android and Robo. The Basic one is where I can make a raw test class with JUnit, the Basic won’t use any library of Android or doesn’t need to be injected by anything to run. These tests run fast because they run directly in JVM with no GUI action required or Android AVD required.

The Android test is where I have to let my code run in Android AVD for integration test, for example sending keys to Activity, invoking click action on control or checking current status of controls.

The Robo test is where I would like to inject or mock object so that I can test the behaviors of app under certain circumstances.

These 3 levels will also be categorized in different test modules to keep the code structure clean and clear.

3. Basic unit test (JUnit test)

To create a unit test in Intellij IDEA, in the Project Tool Windows, right click on the project you want to write a unit test and choose New –> Module

New module

In New Module window, select Test Module then give a module name. Note that the tested project will be selected as default

Intellij IDEA New Module

In this demo, I choose unittests as module name and click on Finish. In Project Tool Window, a new module will be created inside of tested project

Unittest Module

Because the Basic unit test uses only JUnit to test the logic of app therefore in this module we will only test the ViewModel layer where the logic locates (No test for Activity or anything requires more than JVM).

For example, a JUnit test for Basic mode ViewModel looks like following

public class MainActivityViewModelTest extends TestCase {

    public void testAdd_3with5_8() throws Exception {
        MainActivityViewModel viewModel = new MainActivityViewModel();
        Assert.assertEquals("8.0", viewModel.Add("3", "5"));
    }

    public void testSubtract_3with5_Minus2() throws Exception{
        MainActivityViewModel viewModel = new MainActivityViewModel();
        Assert.assertEquals("-2.0", viewModel.Subtract("3", "5"));
    }


    public void testMultiply_3with5_15() throws Exception{
        MainActivityViewModel viewModel = new MainActivityViewModel();
        Assert.assertEquals("15.0", viewModel.Multiply("3", "5"));
    }

    public void testDivide_3with5_0_6() throws Exception{
        MainActivityViewModel viewModel = new MainActivityViewModel();
        Assert.assertEquals("0.6", viewModel.Divide("3", "5"));
    }
}

The test methods validates only the functions/methods of ViewModel where only raw Java code is executed. The name of test methods have a format of WHICH_HOW_EXPECT, means
– which functions are going to be tested,
– under which boundary conditions the tests are going to run and
– their expected behaviors or expected results.

Remember that we want to run our test cases only with JUnit so choose correct configurations for them. On the right click on test classes, choose configurations running with JVM only

Run test with JVM

Android API Platform has also contained a version of JUnit (less than version 3.8). Intellij IDEA does only support JUnit from version 3.8, so in Project Structure, move reference to JUnit to the top or at least before reference to Android API Platform

JUnit on top

If you get following error, then just move reference to JUnit above Android API Platform and the error will be gone

!!! JUnit version 3.8 or later expected:

java.lang.RuntimeException: Stub!
	at junit.runner.BaseTestRunner.<init>(BaseTestRunner.java:5)
	at junit.textui.TestRunner.<init>(TestRunner.java:54)
	at junit.textui.TestRunner.<init>(TestRunner.java:48)
	at junit.textui.TestRunner.<init>(TestRunner.java:41)
	at com.intellij.rt.execution.junit.JUnitStarter.junitVersionChecks(JUnitStarter.java:185)
	at com.intellij.rt.execution.junit.JUnitStarter.canWorkWithJUnitVersion(JUnitStarter.java:168)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:54)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Process finished with exit code -3

The Basic unit tests run fast because it requires only JVM. Remember to run test classes with correct configurations, if you choose a wrong configuration (with Android Robo icon) you’ll receive errors. Don’t be panic, click on Edit Configurations… on toolbar

Edit configurations...

Select the false configuration and remove it

Remove configuration

4. Android unit test

In previous part, we leart how to write a JUnit test for testing logic and we all know that’s not enough for creating a stable app. Users are somehow ‘crazy’ 😉 and their inputs always make us surprise. Therefore we need to implement instrumentation tests to simulate the actions of user and check if our apps response correctly to those actions.

For instrumentation tests, we can use the classes of Android or Robolectric or Robotium. In this part we will make some examples with Android test classes and the next part with Robolectric. Maybe I will discuss Robotium in another post because most of advanced test cases can already be done with Robolectric, Roboguice and Mockito.

To make examples with Android test classes, let’s create a Test Module like JUnit test module above, I name this module as androidtests. In this test module, we are going to test Activity with 2 basic test classes of Android ActivityInstrumentationTestCase2 and ActivityUnitTestCase. I cite their definitions from Google for you to review

ActivityInstrumentationTestCase2
This class provides functional testing of a single activity. The activity under test will be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity()) and you will then be able to manipulate your Activity directly. Other options supported by this test case include:
– You can run any test method on the UI thread (see UiThreadTest).
– You can inject custom Intents into your Activity (see setActivityIntent(Intent)).

ActivityUnitTestCase
This class provides isolated testing of a single activity. The activity under test will be created with minimal connection to the system infrastructure, and you can inject mocked or wrappered versions of many of Activity’s dependencies. Most of the work is handled automatically here by setUp() and tearDown().

In demo source code, you’ll find 2 sample test classes which derive from ActivityInstrumentationTestCase2 and ActivityUnitTestCase. The one which derives from ActivityInstrumentationTestCase2 uses Android test functions to simulate the actions of user like select control, enter value, click button… which are exactly suitable for functional tests

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {

    private TextView textViewResult;
    private Activity mainActivity;

    public MainActivityTest() {
        super(MainActivity.class);
    }


    public void testAdd_3with5_8() throws Exception {
        sendKeys("3 ENTER");
        sendKeys("5 ENTER");
        sendKeys("ENTER");
        Assert.assertEquals("8.0", textViewResult.getText().toString());
    }


    public void testMenuItemAboutClick_FromMainActivity_AboutActivityStart() throws Exception {
        Instrumentation.ActivityMonitor activityMonitor = getInstrumentation().addMonitor(AboutActivity.class.getName(), null, false);
        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
        getInstrumentation().invokeMenuActionSync(mainActivity, R.id.main_menu_about, 0);
        Activity a = getInstrumentation().waitForMonitorWithTimeout(activityMonitor, 1000);
        assertEquals(true, getInstrumentation().checkMonitorHit(activityMonitor, 1));
        a.finish();

    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mainActivity = getActivity();
        textViewResult = (TextView) mainActivity.findViewById(R.id.textViewResult);
    }
}

We can either call sendKeys() function to make sequential inputs or call getInstrumentation() with combinations of other helper functions such as sendKeyDownUpSync(), invokeMenuActionSync(), waitForMonitorWithTimeout(), checkMonitorHit() to make random inputs. In example code above, the AboutActivity which can be invoked through ActionBar, is tested by getInstrumentation() and Co. or the Add button is checked by sendKeys to see if it’s default button when user gives input and simply press enter.

Note that it’s very important that we run the test with correct configuration. To be sure that you run Android test cases with Android Virtual Machine (AVM) because the test cases for Activity require Androind API function. That means the configuration with Android Robo icon must be selected for these test cases.

Run test cases in AVM

The second test class bases on ActivityUnitTestCase. As derived from ActivityUnitTestCase this test class can only provide an isolated testing. We should only access the controls inside of activity and perform the simulated user actions for testing

public class MainActivityTest_1 extends ActivityUnitTestCase<MainActivity> {

    private Activity mainActivity;
    private EditText editTextOperand1;
    private EditText editTextOperand2;
    private Button buttonSubtract;
    private TextView textViewResult;

    public MainActivityTest_1() {
        super(MainActivity.class);
    }

    public void testTextViewResult_DefaultHint_SameWithResourceString() throws Exception {
        Assert.assertEquals(mainActivity.getResources().getString(R.string.text_BasicMode), textViewResult.getHint().toString());

    }

    public void testSubtract_3with5_Minus2() throws Exception{
        editTextOperand1.setText("3");
        editTextOperand2.setText("5");
        buttonSubtract.performClick();
        Assert.assertEquals("-2.0", textViewResult.getText().toString());
    }

    protected void setUp() throws Exception {
        super.setUp();
        startActivity(new Intent(getInstrumentation().getTargetContext(), MainActivity.class), null, null);

        mainActivity = getActivity();
        textViewResult = (TextView)mainActivity.findViewById(R.id.textViewResult);
        editTextOperand1 = (EditText)mainActivity.findViewById(R.id.editTextOperand1);
        editTextOperand2 = (EditText)mainActivity.findViewById(R.id.editTextOperand2);
        buttonSubtract = (Button) mainActivity.findViewById(R.id.buttonSubtract);

    }
}

In example code above, there are 2 sample test functions for checking if the hint of a text view is correct (for example grammar check) and checking if the minus function is correctly calculated and the result is exactly in format that we desire.

The test cases are simple but to make it run, remember to check that the order of the reference and their scope must be correct

Scope

The Android test cases provide us some powerful basic instrumentation test classes so that we can simulate the user actions and execute the UI tests. However sometimes we need some more advanced test cases in which we can inject, fake or mock values. That can’t be done with Android test and we need some advanced frameworks for it.

5. Robolectric, Roboguice, Mockito

5.1 Introduction

JUnit and Android test provide us the ability to make test cases for logic and instrumentation but we can’t inject or mock any thing with these test classes. That means, the implemenation is fixed and we can’t not change anything without breaking the system completely.

Let’s imagine a simple use case, you have a small app reading data from a weather service. Your app runs stably until a day your weather service decides to bring their structures to new version. The old version still works but they won’t provide new features more in old version. Now you would like to try new version with you app and that’ll be a nightmare you didn’t equip your app with Dependency Injection. You have to replace your old implementation with the new one and then you have to test your app again. If everything is fine, you’re God. If not, you’re dead because you have to bring your old implementation back. Moreover you have no possibility to make your app to be compatible with two versions of weather service at the same time.

Therefore back to my Android sample codes, I always try to introduce Roboguice in every sample code post. It’s a preparation for future and test cases later. Roboguice provides a Dependency Injection container where we can define the dependencies and then later decouple the dependencies with other implementations if we want. It really makes more sense when we would like to write test cases for our app, because we can inject and mock any object that we want to change their behavior during testing. The more flexible we are, the more bugs we can cover in our tests.

In this part we’ll take a look at a combination of 3 open source frameworks : Roboguice, Robolectric and Mockito. The Roboguice is a Dependency Injection container. Robolectric is a testing framework and helps us to manage life cycle of our test objects and make them ready for injection. Mockito is here for mocking property’s value which helps us to use a current implementation but with a fix value.

5.2 Roboguice

If you follow my posts before about Android, you must be very familiar with Roboguice. The magic and configuration of Roboguice are simply defined in two important components : Application and Module

public class IOCApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();    //To change body of overridden methods use File | Settings | File Templates.
        RoboGuice.setBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(this), new IOCModule());

    }

}

public class IOCModule implements Module {
    @Override
    public void configure(Binder binder) {
        binder.bind(IMainActivityViewModel.class).to(MainActivityViewModel.class);
        binder.bind(IAdvanceActivityViewModel.class).to(AdvanceActivityViewModel.class);
        binder.bind(ISettings.class).to(DefaultSettings.class);
    }

}

Here is where the dependencies are coupled or where the real implementations are defined. In sample code above, the IAdvanceActivityViewModel is bound to concrete implementation AdvanceActivityViewModel. That means when the app runs and he needs to use an instance of IAdvanceActivityViewModel, Roboguice will initialize an instance of AdvanceActivityViewModel and give it back to program.

That’s is a big advantage because we just need to make another implementation, change the binding here and we can easily couple the new implementation with our system. We’ll make a sample of coupling new implementation within test module in next section.

5.3 Robolectric

5.3.1 Normal test

As discussed above, we’re going to make an Dependency Injection test case for IAdvanceActivityViewModel but I would like to show first that Robolectric is also able to make a normal test without any injection.

We’ll make a simple instrumentation test case for AdvanceActivity. The AdvanceActivity was prepared for injecting code by using Roboguice. All components of this Activity are initialized by injected, none of them is directly defined by new keyword.

public class AdvanceActivity extends RoboActivity {

    @InjectView(R.id.editTextOperand1)
    private EditText editTextOperand1;
    @InjectView(R.id.buttonSqrt)
    private Button buttonSqrt;
    @InjectView(R.id.buttonFactorial)
    private Button buttonFactorial;
    @InjectView(R.id.buttonLogarith)
    private Button buttonLogarith;
    @InjectView(R.id.buttonTenPower)
    private Button buttonTenPower;
    @InjectView(R.id.textViewResult)
    private TextView textViewResult;
    @Inject
    IAdvanceActivityViewModel viewModel;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.advance);

        initializeComponent();
    }
...

To test this activity, we can’t use JUnit or Android test because the dependencies won’t never be built during their test life cycles. Thus they are always null. We need a framework so that we can manage the test life cycle as well as the dependencies. That is why Robolectric comes to use. Robolectric will automatically look up Application and Module where the dependencies are defined and initialize them before test cases run.

Create a test module like JUnit or Android test above, I name it as robotests. Download the libs from their homepages and add references to them. Remember that the order of references and their scope make sense.

Robo dependencies

A simple instrumentation test case with Robolectric and Roboguice will look like following

@RunWith(RobolectricTestRunner.class)
public class AdvanceActivityTest {

    private AdvanceActivity advanceActivity;

     @Test
    public void testTextViewResult_DefaultHint_SameWithResourceString() throws Exception {
        TextView textViewResult = (TextView)advanceActivity.findViewById(R.id.textViewResult);
        Assert.assertEquals(advanceActivity.getResources().getString(R.string.text_AdvanceMode), textViewResult.getHint().toString());

    }

    @Before
    public void setUp() throws Exception {
        advanceActivity = Robolectric.buildActivity(AdvanceActivity.class).create().get();
    }
}

It’s simply simple, isn’t it. We don’t have to locate the dependencies. Robolectric will take a look in source code, find where the dependencies defined and load them. However because we’re testing Android API, be sure that you use correct configuration with AVM for test cases.

As you can see, we can also use Robolectric for making instrumentation tests. It’s just a thing of favor. You can use either Android test or Robolectric or both of them for your functional test cases. They are all good. Of course, Android test work only if you don’t use any Dependency Injection (DI) in your testing object. If you are using DI, you have to use Robolectric.

5.3.2 Injection

The AdvanceActivity in demo app uses IAdvanceActivityViewModel for calculation, we will replace current implementation AdvanceActivityViewModel with new InjectedAdvanceActivityViewModel. That can only be done by redefining Application and Module.

In the test module, create a test Application with same structure as Application in main project, be sure that the test Application has a prefix Test so that the convention works (Roboguice will check if there is any Application which locates in same structure and has a prefix Test. If he finds any, he will load that Application instead of original one)

Test Application

In the new TestIOCApplication, we can redefine our Module as well as the dependencies

public class TestIOCApplication extends Application implements TestLifecycleApplication {


    @Override
    public void beforeTest(Method method) {

    }

    @Override
    public void prepareTest(Object o) {
        MockitoAnnotations.initMocks(o);
        TestIOCApplication application = (TestIOCApplication) Robolectric.application;
        RoboGuice.setBaseApplicationInjector(application, RoboGuice.DEFAULT_STAGE,RoboGuice.newDefaultRoboModule(application), new InjectedModule());
        RoboGuice.getInjector(application).injectMembers(o);
    }

    @Override
    public void afterTest(Method method) {

    }


}

The original IOCModule was replaced by InjectedModule(). In the InjectedModule() we just need to inject our new implementation for IAdvanceActivityViewModel

public class InjectedModule implements Module {
    @Override
    public void configure(Binder binder) {
        binder.bind(IAdvanceActivityViewModel.class).to(InjectedAdvanceActivityViewModel.class);
        binder.bind(ISettings.class).to(DefaultSettings.class);
    }

}

Now when we run our test for IAdvanceActivityViewModel, the implementation will be completely redefined with our new injected InjectedAdvanceActivityViewModel

@RunWith(RobolectricTestRunner.class)
public class AdvanceActivityTest_Inject {

    @Inject
    private IAdvanceActivityViewModel advanceActivityViewModel;

    @Test
    public void testAdvanceSqrt_4IsOperand_MustBeValueOfInjected() throws Exception {
        Assert.assertEquals("0",advanceActivityViewModel.Sqrt("4"));
    }



    @Before
    public void setUp() throws Exception {
//        RoboGuice.setBaseApplicationInjector(Robolectric.application, RoboGuice.DEFAULT_STAGE,RoboGuice.newDefaultRoboModule(Robolectric.application), new InjectedModule());
//        RoboInjector injector = RoboGuice.getInjector(Robolectric.application);
//        injector.injectMembersWithoutViews(this);

        Assert.assertNotNull(advanceActivityViewModel);

    }
}

In practice it’s extremely useful because we can easily test our new implementations without breaking our app. The new implementation will be tested until everything is fine inside test module meanwhile the app can run with the old one. When test is finish, we just need to replace the new implementation at binding code and the system runs smoothly.

5.4 Mockito

Code Injection is a nice feature when we would like to replace a current implementation with the new one. But you can also recognize its disadvantage that we have to implement the interface completely. Sometimes all we need is just use a current implementation but some values of some properties should be “casted” to fixed values. What we want in that case can be solved by using Mocking framework.

A mocking framework helps us to create an instance and override the method or property behavior with something else during testing. In this demo, we’ll use Mockito as our mocking framework.

@RunWith(RobolectricTestRunner.class)
public class AdvanceActivityTest_Mock {

    private ISettings settings = mock(ISettings.class, RETURNS_DEEP_STUBS);

    @Inject
    private IAdvanceActivityViewModel advanceActivityViewModel;

    @Test
    public void testFactorial_WithMockedSettings_MustBeAlways2() throws Exception {
        Assert.assertEquals("-1",advanceActivityViewModel.Factorial("4"));
    }

    @Before
    public void setUp() throws Exception {
        RoboGuice.setBaseApplicationInjector(Robolectric.application, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(Robolectric.application), new MockedModule());
        RoboInjector injector = RoboGuice.getInjector(Robolectric.application);
        injector.injectMembersWithoutViews(this);

        Assert.assertNotNull(advanceActivityViewModel);

        when(settings.getFactorialSetting()).thenReturn(1);

    }

    public class MockedModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(IAdvanceActivityViewModel.class).to(AdvanceActivityViewModel.class);
            bind(ISettings.class).toInstance(settings);
        }
    }
}

Mockito is easy to use. In sample code above, we just need to redefine Application and Module for Roboguice where we redefine the binding between interface and real implementation. It’s almost same as Dependency Injection, the only difference is that instead of binding to a concrete implementation, we’ll bind to a concrete instance (ISettings will bind to settings object).

This concrete instance was already mocked by Mockito and the return value of function getFactorialSetting() was mocked with a fixed value. That means any call to this function inside the test class will get this fixed value no matter how the inputs are.

The mocking technique is very useful when we want to check how the settings have effect on our app, for example the current culture, current resolution, current RAM… We can mock the value and see how our app behaves.

6. Conclusion

JUnit test, Android test and Robo test make a powerful testing system for Android development. Depends on purposes of tests as well as requirements of test performance, we should choose an appropriate test type to optimize the test process. For the consequence, we can minimize the bugs will be found in our apps and our lives just get happier. The sample code is kept simple to illustrate how the frameworks work. In practice the tests may be much more complex, but for learning purpose I think it’s a good example :). The source code can be downloaded from following link

Source code: https://bitbucket.org/hintdesk/android-basic-testing-with-unit-test-android-test-and

Leave a Reply

Your email address will not be published. Required fields are marked *