Модульное тестирование BroadcastReceiver

Я пытался выяснить, как провести модульное тестирование моего BroadcastReceiver, и я просмотрел StackOverflow и другие веб-сайты, но не могу найти решение своей проблемы.

В моей mainActivity у меня есть следующие две функции:

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);
        }
    }
};

RegisterNetRcvr вызывается из функции onResume (и в равной степени у меня есть unregister, вызываемый из onPause).

Как видно из вышеизложенного, у меня есть функция (handleBcastReceiver), которая вызывается для обработки события onReceive, и, таким образом, у меня есть другой класс, который затем имеет множество частных функций, которые вызываются из этой функции.

Просто чтобы немного усложнить ситуацию... при вызове функции onReceive, как подробно описано выше, функция "handleBcastReceiver" затем должна будет "получить" правильные данные... и, таким образом, сделать вызов для фактического получения соответствующей системы данные следующим образом:

private static NetworkInfo getWiFiNetworkInfo(Context context) {

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

Я считаю, что я должен эмулировать поведение обратного вызова onReceive, но также, я думаю, эмулировать то, что будет возвращено системой во время вызова getWiFiNetworkInfo.

Я думаю, что для этого я должен использовать RoboElectric, поскольку он может скрывать BroadcastReceiver/ConnectivityManager. Я также посмотрел на Mockito, однако я считаю, что RoboElectric — это то, что мне нужно для этого, но я допускаю, что вполне могу ошибаться. Насколько мне известно, RoboElectric позволяет мне эмулировать «систему Android», а Mockito позволяет мне издеваться над своими собственными классами.

В настоящее время я просто не вижу, как протестировать BroadcastReceiver. Кроме того, я не совсем понимаю, должен ли я запускать эмулятор для этого или просто «запустить» свой модульный тест, поскольку «тень» должна содержать все, что мне нужно. Наконец, если бы я должен был затенить BroadcastReceiver, как мне заставить WIFI быть «включенным» или «отключенным» с помощью этого теневого процесса? На самом деле, должен ли я эмулировать BroadcastReceiver или просто ConnectivityManager... Я искренне запутался!

Что я сделал до сих пор, так это создал класс BroadcastReceiverTest в тестовом разделе моего приложения (а не в разделе «androidTest»). Я могу выполнять обычные простые модульные тесты для функций, я просто застрял в том, как эмулировать это «системное» поведение.

Как всегда, любая помощь приветствуется.


person greysqrl    schedule 16.01.2017    source источник
comment
Есть проблема - чтобы протестировать приложение для Android, вам нужно исправить его определенным, тестируемым способом. Вот почему MVP и подобная техника сейчас очень популярны.   -  person Divers    schedule 16.01.2017
comment
Пара проблем с BroadcastReceiver. Таргетинг CONNECTIVITY_CHANGE устарел для приложений с таргетингом N или выше. Также получатель должен проверить действие, на которое он подписан, чтобы избежать злонамеренных запросов.   -  person Oren Bochman    schedule 14.11.2018


Ответы (1)


Итак, я думаю, что решил это... :) Если кто-то с большим опытом хочет дать мне отзыв по этому поводу, я был бы очень признателен.

Я чувствую, что мне не нужно больше заботиться о «широковещательном приемнике», поэтому мне нужно беспокоиться о затенении ConnectivityManager, поскольку это то, из чего в конечном итоге будет считываться функция «handleBcastReceiver». Итак, на самом деле это то, что важно, поскольку именно «подача» в мои функции определяет их поведение. Когда функция onReceive запускается, все, что она будет делать, это функция «handleBcastReceiver», а затем обновлять пользовательский интерфейс с результатом, и, следовательно, это не то, что мне действительно нужно здесь тестировать.

Я создал следующее, и это работает для меня. Если я изменю свои тестовые данные, тесты будут неудачными, поэтому я считаю, что это удовлетворительно. Я надеюсь, что это может быть полезно другим, и, как я уже упоминал, я очень рад получить отзывы о том, правильно ли то, что я сделал, или нет.

@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);
    }
}

У меня, конечно, есть больше, чем просто этот тест, но я чувствовал, что этого будет достаточно, чтобы помочь всем, кто так же застрял в этом :)

Приятно сказать, что в конце концов мне удалось протестировать прослушиватель BroadcastReceiver... если вы хотите знать, как это сделать...

@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());

}
person greysqrl    schedule 16.01.2017
comment
Я должен отметить, что многое из того, что я узнал, было взято из этого поста... connection-with-robo" title="shadownetworkinfo всегда имеет тип mobile при тестировании Wi-Fi-соединения с robo"> stackoverflow.com/questions/30625378/ - person greysqrl; 16.01.2017