Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing a BroadcastReceiver

I've been trying to figure out how to unit test my BroadcastReceiver and I have looked at StackOverflow and other websites but I can't find the solution to my problem.

In my mainActivity I have the following two functions:

private void registerNetRcvr(){

    if (!isRcvrRegistered) {        
        isRcvrRegistered = true;
        registerReceiver(receiver, new IntentFilter("android.net.wifi.STATE_CHANGE"));
        registerReceiver(receiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));
    }
}

private BroadcastReceiver receiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {

        NetworkHandler.NetworkInfo netInfo = NetworkHandler.handleBcastReceiver(context);

        if (netInfo!=null){
            handleInfoChange(netInfo);
        } else {
            handleInfoChange(null);
        }
    }
};

The registerNetRcvr is called from within the onResume function (and equally I have an unregister called from onPause).

As can be seen from the above, I have a function (handleBcastReceiver) that is called to handle the onReceive event and thus have another class that then has a variety of private functions which are called from this function.

Just to slightly complicate matters... upon the onReceive function being called, as detailed above the 'handleBcastReceiver' function would then need to 'retreive' the correct data... and, as such would make a call to actually retrieve the appropriate system data as follows:

private static NetworkInfo getWiFiNetworkInfo(Context context) {

    ConnectivityManager connManager = (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
    return connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
}

I believe that I should emulate the behaviour of the onReceive callback but also I guess emulate what would be returned from the system during the call to getWiFiNetworkInfo.

My thinking is that I should be using RoboElectric to do this as it can shadow the BroadcastReceiver/ConnectivityManager. I've also looked at Mockito however I believe that RoboElectric is what I require for this but I accept that I could well be wrong. As far as I'm aware, RoboElectric allows me to emulate 'the Android system' and Mockito would allow me to mock my own classes.

At present I just can't see how to test the BroadcastReceiver. Additionally I'm not clear upon whether I should be running the emulator to do this or simply 'run' my unit test as the 'shadow' should contain everything I need. Finally, if I was to shadow the BroadcastReceiver, how do I get WIFI to be 'enabled' or 'disabled' through this shadow process? In fact, should I be emulating a BroadcastReceiver or just the ConnectivityManager... I'm genuinely confused!

What I have done so far is to create a BroadcastReceiverTest class within the test section of my app (not the 'androidTest' section). I can do normal simple unit tests on functions, I'm just stuck with how to emulate this 'system' behaviour.

As always, any help is greatly appreciated.

like image 484
greysqrl Avatar asked Jan 16 '17 09:01

greysqrl


People also ask

What is a BroadcastReceiver?

Android BroadcastReceiver is a dormant component of android that listens to system-wide broadcast events or intents. When any of these events occur it brings the application into action by either creating a status bar notification or performing a task.

What is the life cycle of BroadcastReceiver?

A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent) . Once your code returns from this function, the system considers the object to be finished and no longer active.

What are advantages of BroadcastReceiver?

A Broadcast receiver wakes your application up, the inline code works only when your application is running. For example if you want your application to be notified of an incoming call, even if your app is not running, you use a broadcast receiver.

What is the difference between broadcast receiver and a service?

A Service receives intents that were sent specifically to your application, just like an Activity. A Broadcast Receiver receives intents that were broadcast system-wide to all apps installed on the device.


1 Answers

So, I think I've solved it... :) If anyone with more experience wishes to give me feedback on this, it'd be much appreciated.

I feel that I don't need to care about the 'broadcast receiver' more so I need to be bothered with shadowing the ConnectivityManager as this is what the 'handleBcastReceiver' function will ultimately read from. So, really that is what is important as it's the 'feed' into my functions which drives how they behave. When the onReceive function is triggered, all it will do is 'handleBcastReceiver' function and then update the UI with the result, and hence it's not what I really need to test here.

I created the following and it's working for me. If I modify my test data, tests will fail as appropriate so I believe that this is satisfactory. I hope others may find it useful and as I mentioned, I'm very happy to receive feedback on whether what I've done is or isn't correct.

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23, manifest = "/src/main/AndroidManifest.xml")
public class NetworkStateHandlerUnitTest {

    private static String NETWORK_TYPE_HSDPA = "HSDPA";
    private ConnectivityManager connectivityManager;
    private ShadowConnectivityManager shadowConnectivityManager;
    private Context context;

    @Before
    public void setUp() {

    }

    @Test
    public void handleBroadcastReceiverWIFIConnected() {

        context = RuntimeEnvironment.application.getApplicationContext();
        connectivityManager = getConnectivityManager();
        shadowConnectivityManager = Shadows.shadowOf(connectivityManager);

        //Create shadow Network Info - Connected, WIFI, No Subtype, available, connected
        NetworkInfo networkInfoShadow = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.CONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, true);
        shadowConnectivityManager.setNetworkInfo(ConnectivityManager.TYPE_WIFI, networkInfoShadow);

        NetworkHandler.NetworkInfo networkInfoResponse;

        //Call the function under test
        networkInfoResponse = NetworkHandler.handleBcastReceiver(context);

        //Validate the response
        assertEquals(networkInfoResponse.getNetworkType(), ConnectivityManager.TYPE_WIFI);
    }

    private ConnectivityManager getConnectivityManager() {
        return (ConnectivityManager) RuntimeEnvironment.application.getSystemService(context.CONNECTIVITY_SERVICE);
    }
}

I do, of course have more than just this one test but felt this would be enough to help anyone else who finds themselves similarly stuck with this :)

Pleased to say that eventually I managed to test the BroadcastReceiver listener as well... in case you want to know how to do this...

@Test
public void handleBrcastRcvrWifiConn() {

    MainActivity activity = Robolectric.setupActivity(MainActivity.class);
    TextView tvConnStatus = (TextView) activity.findViewById(R.id.tvConnectionState);


    context = RuntimeEnvironment.application.getApplicationContext();
    connectivityManager = getConnectivityManager();
    shadowConnManager = Shadows.shadowOf(connectivityManager);

    //Create shadow Network Info - Connected, WIFI, No Subtype, available, connected
    NetworkInfo netInfoShadow = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.CONNECTED,
            ConnectivityManager.TYPE_WIFI, 0, true, true);
    shadowConnManager.setNetworkInfo(ConnectivityManager.TYPE_WIFI, networkInfoShadow);


    //Trigger BroadcastReceiver
    RuntimeEnvironment.application.sendBroadcast(new Intent("android.net.wifi.STATE_CHANGE"));

    //Validate result by checking value of textView
    assertEquals("Connected", tvConnectionState.getText());

}
like image 50
greysqrl Avatar answered Sep 24 '22 07:09

greysqrl