Showing posts with label test. Show all posts
Showing posts with label test. Show all posts

Sunday, October 28, 2012

Android Service test - callback method not firing

Preface
If you want to execute a long running operation in your program Service is a fine choice. You might ask why not using AsyncTask? The answer to it is simple. AsyncTask is for short operations (few seconds probably) and Service is for longer or more CPU intensive jobs. For better and easier integration, it’s better to test each unit of your program. Android JUnit provides set of classes for writing unit tests. ServiceTestCase is a class by which you can unit test your service class.

Scenario
Consider we have to send some data for a complex calculation to a remote server and wait for the response. We decided to use Service. In our example when user initiates the calculation our service start in the background and send the data to the server. Once the result is back it will update the application local database. Lets have a unit test for our Service.

Problem
Our service is an IntentService and it receives a serviceCallback (ResultReciever). The problem is, before the callback method (onReceiveResult) is called the teardown method of the our unit test terminates the test.

Solution
A solution to this problem could be somehow forcing the main thread to wait for the result of the callBack service method to return. For doing this we can make use of CountDownLatch. This class helps us to pause the thread until one or more operations finishes. Below is the ServiceTestCase utilizing the aforementioned mechanism.

await method of CountDownLatch cause the current thread to wait. When the response is back,  countDown method of CountDownLatch counts down and releases all the waiting threads.


 import java.util.HashMap;  
 import java.util.Map;  
 import java.util.Properties;  
 import java.util.concurrent.CountDownLatch;  
 import android.content.Intent;  
 import android.os.Bundle;  
 import android.os.ResultReceiver;  
 import android.test.ServiceTestCase;  
 public class CalculationServiceTest extends ServiceTestCase<CalculationService>{  
      public CalculationServiceTest() {  
           super(CalculationService.class);  
      }  
      public void testCalculation() throws InterruptedException{  
           //our synchronization aid   
           CountDownLatch signal = new CountDownLatch(1);  
           //passing object for evaluating the result of our service  
           Map<String, String> result = new HashMap<String, String>();  
           //Creating a new thread which is responsible for running our Service  
           new Thread(new WorkerRunnable( signal, result)).start();  
           //casuing the current to wait  
           signal.await();  
           assertEquals(CommunicationConstants.SERVICE_RESULT_SUCCESS, (String)result.get("result"));  
      }  
      class WorkerRunnable implements Runnable {  
           private  CountDownLatch doneSignal;  
           private Map<String, String> result;  
           public WorkerRunnable(CountDownLatch cdl, Map<String, String> result){  
                this.doneSignal = cdl;  
                this.result = result;  
           }  
           @Override  
           public void run() {  
                Intent startServiceIntent = new Intent();  
                startServiceIntent.putExtra(CalculationService.SERVICE_TYPE,  
                          Constant.CALCULATE);  
                ResultReceiver serviceCallback = new ResultReceiver(null) {  
                     @Override  
                     protected void onReceiveResult(int resultCode, Bundle resultData) {  
                          result.put("result", String.valueOf(resultCode));  
                          doneSignal.countDown();  
                     }  
                };  
                //setting a callback for our service  
                startServiceIntent.putExtra(CalculationService.SERVICE_CALLBACK,  
                          serviceCallback);  
                startServiceIntent.setClass(getContext(), CalculationService.class);  
                startService(startServiceIntent);  
           }  
      }  
 }  

Monday, August 20, 2012

Android testing framework - Robotium


Robotium is an Android UI testing framework designed to simplify black-box testing. The framework supports almost all Android related classes such as Activities, Dialogs, Menus, etc. Robotium can be seen as a supplementary to JUnit when you want to automate your tests for Android applications.

When using Robotium, the framework interacts directly with the emulator. It just like if a real user is using your software. You can create the steps for getting the result, identify the expected result and finally, assert the outcome.

There is another testing framework called Robolectric. This framework does not need an emulator to be present. It contains implementation of Android SDK within itself. This speeds up the testing process.

Now, an example using Robotium.

Lets create a sample ui with 2 EditTexts and one DatePicker.
Assume that we have to fill these 2 editTexts and set the calendar to January 1 2011.

Create a class which extends ActivityInstrumentationTestCase2. This class give you the capability to setup a fully functional runtime environment.

Create a no argument constructor and call the super constructor with the Activity under test.

 ActivityInstrumentationTestCase2 defines activity under test which is, in this example MainActivity
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity>

private Solo solo;

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

Create a Solo object in setUp method.

@Override 
protected void setUp() throws Exception {
        super.setUp();
By using the method below we can get a reference to the activity under test and starting it if necessary.
        currentActivity = getActivity();
Access Robotium using solo object instantiate it by passing instrumentation and the activity under test
        solo = new Solo(getInstrumentation(), currentActivity);
}

Create a test method for filling the UI.

public void testFillMainActivityUi(){
         String text1 = "first editText";
         String text2 = "second editText";
         get a reference to editTexts
         EditText editText1 = (EditText) currentActivity.findViewById(R.id.editText1);
         EditText editText2 = (EditText) currentActivity.findViewById(R.id.editText2);
        set text for both editTexts
        solo.enterText(editText1, text1);
        solo.enterText(editText2, text2);
        get a reference to datePicker
        DatePicker datePicker = (DatePicker)currentActivity.findViewById(R.id.datePicker1);
        set datePicker value to first Jan 2011 (month starts from zero)
        solo.setDatePicker(datePicker, 2011, 0, 1);
        assert the result
        Assert.assertEquals(text1, editText1.getText().toString());
        Assert.assertEquals(text2, editText2.getText().toString());


        Assert.assertEquals(2011, datePicker.getYear());
        Assert.assertEquals(0, datePicker.getMonth());
        Assert.assertEquals(1, datePicker.getDayOfMonth());
}