dinsdag 11 februari 2014

Robolectric

At a certain point in time in your life you realise that you're done fooling around (in the technical sense), and that quality - next to Love - conquers all. In the past weeks, I had an epiphany and realised I had to focus more on test driven and continuous development in order to improve the quality of (my) Android coding.

Thus started my personal quest "How is Test Driven Development (TDD) accomplished on Android". All Java-lovers including myself will somehow utter the words Unit Testing and Continuous Integration. True, but how do you manage this on a platform which is mobile and where device emulators, well, frankly s*ck.




I finally ended up with Robolectric.org, a framework that allows you to run your code against your JVM. Yes, your JVM.  Anxious as a 6-year-old that just got some presents from Santa, I started coding.

... and got stuck. Where the hell is the documentation? Oh right, Stackoverflow.

Frustrated about the copy-pasting from Stackoverflow, I wanted to share some of the examples I found/used that hopefully will ease-up your life, and help you develop up-spec quality Android applications.

Imaging that you developed an activity that fetches information from the Extras bundle passed in your intent:
public class MyActivity extends Activity {

    private final static String TAG = MyActivity.class.getSimpleName();


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.my_layout);



        Bundle bundle = getIntent().getExtras();



        String resource_url  = (bundle == null ? "" : bundle.getString("URL", ""));

        String resource_name = (bundle == null ? "" : bundle.getString("RESOURCE", ""));




        ((TextView) this.findViewById(R.id.txt1)).setText(resource_name);

        ((TextView) this.findViewById(R.id.txt2)).setText(resource_url);


    }


}


You would like to know that when you pass the information via the intent, that it will place the corresponding strings in the foreseen TextViews.


package ...
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.shadows.ShadowActivity;
import org.robolectric.util.ActivityController;

import ...



@RunWith(RobolectricGradleTestRunner.class)
public class MyActivityTest {
    @Before
    public void setUp() {
       //whatever you need to have done before a test is run
    }    

    @Test
    public void testIfTextFieldsAreFilled() {
        Bundle bundle = new Bundle();
        bundle.putString("URL", "http://www.google.be");
        bundle.putString("RESOURCE", "Google");

        Intent intent = new Intent(Robolectric.getShadowApplication().getApplicationContext(), MyActivity.class);
        intent.putExtras(bundle);

        Activity activity = Robolectric.buildActivity(MyActivity.class).withIntent(intent).create().get();
        ShadowActivity testActivity = Robolectric.shadowOf(activity);

        Assert.assertEquals("Google", ((TextView) testActivity.findViewById(R.id.txt1)).getText().toString());         
        Assert.assertEquals("http://www.google.be", ((TextView) testActivity.findViewById(R.id.txt2)).getText().toString());
    }

}


There is actually quite an important difference. You would expect that you would test against the "built" Activity, but you need to invoke the ShadowActivity via Robolectric.shadowOf(...);. If you don't, your test code will not pass the intent correctly and your test results will fail.

        
        Activity activity = Robolectric.buildActivity(MyActivity.class).withIntent(intent).create().get();
        ShadowActivity testActivity = Robolectric.shadowOf(activity);

Happy testing! Happy Developer! Happy Customer!

Geen opmerkingen:

Een reactie posten