How to unit test an onFocusChangeListener … don’t.

I am fairly new to unit testing in practice. I’ve read about it over the last couple of years and even given a talk on different frameworks for python, but I’ve never really dug into it enough to use it in my daily practice.  At work we are starting a new mobile project and I decided to pull some of my reusable code out to a library project. While doing this, I thought it would be a good idea to start putting everything under unit test. In just a few short days I have learned a tremendous amount and found a couple of bugs that could have come back to bite me In the future.

The title of this post is “How to unit test an onFocusChangeListener … don’t”. What do I mean by that? I have a custom Android view which extends EditText to format a phone number based on a certain mask. I wrote a test which should have fired the onFocusChangeListener. I’m using Robolectric to do my testing and couldn’t get the listener to fire. I looked online and found a great comment from one of the Roboletric devs which say you shouldn’t be testing the listener. As a developer writing unit test, you aren’t testing that the platform works. You are testing that YOUR code works. I realized that I should be testing the method call to the formatPhoneNumber() method, not trying to trigger the actual listener. One problem I had was the formatPhoneNumber() method was private. So how do get to that method to test it?  The answer is reflection. *Note: creation of the Method and PhoneNumberEdit are repeated due to needing to reset the shared preferences before the PhoneNumberEdit is created. Other test in the test class use the default objects created in setUp(). I wanted to show how the activity is build with Robolectric.

The signature for my formatPhoneNumber() method:

private String formatPhoneNumber(String mString) {…}

Using reflection, you can invoke the method:

PhoneNumberEdit numberEdit;

Activity activity;

Method formatMethod;

@Before

public void setUp() {

    activity = Robolectric.buildActivity(Activity.class).create().get();

    numberEdit = new PhoneNumberEdit(activity);

    try {

        formatMethod = numberEdit.getClass().getDeclaredMethod("formatPhoneNumber”);

        formatMethod.setAccessible(true);

    }catch(Exception ex) {

        ex.printStackTrace();
    }

}

@Test

public void testNumberFormattedWithCustomMask() {

    String number = "2055551212";

    SharedPreferences prefs = ShadowPreferenceManager.getDefaultSharedPreferences(activity);

    // reset numberEdit so we have correct prefs.
    prefs.edit().putString("phoneFormat", "555-555-5555").commit();

    numberEdit = new PhoneNumberEdit(activity);

    try {

        formatMethod = numberEdit.getClass().getDeclaredMethod("formatPhoneNumber");
        formatMethod.setAccessible(true);
    }catch(Exception ex) {
        ex.printStackTrace();
    }

    numberEdit.setText(number);

    try {
        formatMethod.invoke(numberEdit);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    assertEquals("205-555-1212", numberEdit.getText().toString());
}

The lesson learned is when unit testing, be sure your testing is focused and test only the code you care about. You shouldn’t be testing the framework/environment unless that is the code you wrote. Just remember this: Small, focused test and you should be off to a great start.