Макетирование WifiManager для модульного тестирования Android - PullRequest
5 голосов
/ 06 апреля 2011

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

К сожалению, мне было довольно сложно успешно смоделировать WifiManager (хотя я полагаю, что я могу передать нулевые ссылки на его конструктор в моем MockWifiManager). Это будет моей первой проблемой, так как, как только у меня будет MockWifiManager, с которым можно поиграть (если это даже работает!), Мне придется успешно создать свой тест ScanResults, у которого нет публичного конструктора (представьте, что он где-то создан какой-то фабрикой). 1003 *

Вопросы: Если у него нет общедоступного конструктора, могу ли я его расширить?

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

Я очень плохо знаком с android, поэтому пытаясь смоделировать всю эту функциональность, я пытался сказать как минимум.

Спасибо за ваш вклад!

Edit: У меня адское время создания экземпляра MockWifiManager. Конструктор для менеджера Wi-Fi ожидает IWifiManager тип, который, по-видимому, не существует в Android SDK.

Ответы [ 3 ]

9 голосов
/ 09 апреля 2011

Создайте абстракцию вокруг WifiManager. Используйте это для насмешек. Насмешливые вещи, которыми вы не владеете, жесткие и ломкие. Если все сделано правильно, вы сможете сменить внутреннее устройство, плюс у вас получится более привлекательный API.

Для вашего тестирования вы можете заглушить / подделать менеджер, который вам понравился. Для производства вы перейдете в конкретный экземпляр.

Что касается вашей точки зрения об изменении кода, просто чтобы сделать его тестируемым, что является неправильным. Во-первых, вы должны издеваться над ролями, а не над типами, как описано в статье ниже. Google для получения дополнительной информации.

Во-вторых, создание абстракции вокруг стороннего кода является наилучшей практикой, как указано в принципе обращения зависимостей в SOLID. Вы всегда должны зависеть от абстракций, а не от конкретных реализаций, независимо от того, проводите ли вы модульное тестирование или нет.

http://www.objectmentor.com/resources/articles/dip.pdf http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

2 голосов
/ 08 апреля 2011

Вы можете попытаться создать экземпляры ScanResult, используя отражение для доступа к закрытым конструкторам.Код может выглядеть примерно так:

        try {
            Constructor<ScanResult> ctor = ScanResult.class.getDeclaredConstructor(null);
            ctor.setAccessible(true);
            ScanResult sr = ctor.newInstance(null);
            sr.BSSID = "foo";
            sr.SSID = "bar";
            // etc... 
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

Для других способов тестирования я чаще всего преобразую информацию из экземпляров, таких как ScanResult, и инкапсулирую только необходимую мне информацию в свои собственные объекты.Этим я кормлю метод, выполняющий тяжелую работуЭто облегчает тестирование, поскольку вы можете легко создавать эти промежуточные объекты, не полагаясь на реальные объекты ScanResult.

0 голосов
/ 26 октября 2016

Я некоторое время пытался построить ScanResult объект.Я успешно использовал этот подход отражения выше.

Если кто-то ищет способ клонировать ScanResult объект (или любой другой объект, реализующий интерфейс Parcelable), вы можете использовать этот подход (я проверил это прямо вюнит-тест):

@RunWith(RobolectricTestRunner.class)
@Config(manifest=Config.NONE)
public class MovingAverageQueueTests {
    @Test
    public void parcelTest() {
        Parcel parcel = Parcel.obtain();

        ScanResult sr = buildScanResult("01:02:03:04:05:06", 70);

        parcel.writeValue(sr);
        parcel.setDataPosition(0); // required after unmarshalling
        ScanResult clone = (ScanResult)parcel.readValue(ScanResult.class.getClassLoader());
        parcel.recycle();

        assertThat(clone.BSSID, is(equalTo(sr.BSSID)));
        assertThat(clone.level, is(equalTo(sr.level)));
        assertThat(clone, is(not(sameInstance(sr))));
    }

    private ScanResult buildScanResult(String mac, int level) {
        Constructor<ScanResult> ctor = null;
        ScanResult sr = null;

        try {
            ctor = ScanResult.class.getDeclaredConstructor(null);
            ctor.setAccessible(true);
            sr = ctor.newInstance(null);

            sr.BSSID = mac;
            sr.level = level;

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return sr;
    }
}

А что касается производительности, то эта наивная проверка:

@Test
public void buildVsClonePerformanceTest() {
    ScanResult sr = null;

    long start = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        sr = buildScanResult("01:02:03:04:05:06", 70);
    }
    long elapsedNanos = System.nanoTime() - start;

    LOGGER.info("buildScanResult: " + elapsedNanos);

    start = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        sr = cloneScanResult(sr);
    }
    elapsedNanos = System.nanoTime() - start;

    LOGGER.info("cloneScanResult: " + elapsedNanos);
}

Показала эти результаты:

26 октября 2016 г. 3: 25: 19: 00 com.example.neutrino.maze.MovingAverageQueueTests buildVsClonePerformanceTest ИНФОРМАЦИЯ: buildScanResult: 202072179 26 октября 2016 г. 15:25:21 2004391903

Таким образом, клонирование таким способом в 10 раз менее эффективно, чем создание экземпляра даже с отражением.Я знаю, что этот тест не является надежным, так как при компиляции выполняются оптимизации ... Однако фактор десяти трудно смягчить.Я также проверил 10K итераций, а затем фактор был даже 100!Просто для вашей информации.

PS вывод из цикла Parcel.obtain () и parcel.recycle не помогает

...