unit test a service based on BroadcastReceiver getting list of wifi's SSID and returning Rx.Observable

657 Views Asked by At

I am trying to test unit my ServiceHelper class that is providing a list of Wifi's SSID to show in my view. So I am providing an observable using RxJava + RxAndroid. As you can see I have chosen the Observable.create() to create from native my observable (Arraylist of Items) witch is depending on a BroadcastReciever inner class. I need to test unit this method. I don't have a good idea about how to test or mock this kind of service that does depend on Sensors.

public class WifiPhoneServiceHelper {

private Context context;
private ArrayList<String> ssidList = new ArrayList<>();
private List<ScanResult> scanResultList;
private WifiManager wifiManager;

public WifiPhoneServiceHelper(Context context) {
    this.context = context;
}

public Observable<ArrayList<String>> getObservableSsidWifiList() {
    return Observable.create(new Observable.OnSubscribe<ArrayList<String>>() {

        @Override
        public void call(final Subscriber<? super ArrayList<String>> subscriber) {
            wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

            final BroadcastReceiver receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                        scanResultList = wifiManager.getScanResults();
                        for (int i = 0; i < scanResultList.size(); i++) {
                            String ssidDetected = scanResultList.get(i).SSID;
                            ssidList.add(ssidDetected);
                        }
                        subscriber.onNext(ssidList);
                    }
                }
            };

            context.registerReceiver(receiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

            if (!wifiManager.isWifiEnabled()) {
                wifiManager.setWifiEnabled(true);
            }
            wifiManager.startScan();

            subscriber.add(new MainThreadSubscription() {
                @Override
                protected void onUnsubscribe() {
                    context.unregisterReceiver(receiver);
                    Timber.d("unregisterReceiver BroadcastReceiver SCAN_RESULTS_AVAILABLE_ACTION");
                    if (!subscriber.isUnsubscribed()) {
                        subscriber.unsubscribe();
                    }
                }
            });
        }
    });
}
2

There are 2 best solutions below

4
On

In order to unit test your code you need to description of all functionality into separate interface and then implement it for Android and create mock implementation for unit tests (Mockito).

3
On

Alex effectively suggests that you create an interface between your code and the Wi-Fi system, and in production code have it implemented by Android's WifiManager, but in testing, implemented by your own test harness.

You could certainly do that but another option is Robolectric. This is an Android unit test framework that can be used to mock large parts of the Android system without doing as much groundwork as you would with Mockito. Specifically it can mock the Wi-Fi system and therefore you can have it pretend that a given network exists or doesn't. The advantages to this are you can largely leave your Wi-Fi using code as it is, without heavy refactoring for test, but you still don't need a real device and it's not dependent on the real world's networking environment.

In particular look for examples based around ShadowWifiManager. The other half of the puzzle is producing event broadcasts to stimulate your code, but this should also be feasible.

It's beyond the scope of this answer to fully describe the establishment of a Robolectric test environment, but hopefully that gives you something to begin researching around.