Модульные тесты Android с несколькими потоками - PullRequest
6 голосов
/ 10 октября 2011

У меня проблема с модульными тестами в Android.

Мой объект MyObject имеет метод start(), например:

public void start() {
    final Handler onStartHandler = new Handler();
    new Thread() {
        @Override
        public void run() {
            super.run();

            onStartHandler.post(new Runnable() {
                @Override
                public void run() {
                    mIsRunning = true;
                    onStart();
                }
            });
        }
    }.start();
}

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

public void testOnStartIsCalled() {
    assertFalse("onStart() should not be called", mMyObject.isRunning());
    mMyObject.start();
    assertTrue("onStart() should be called", mMyObject.isRunning());
    mMyObject.stop();
    assertFalse("onStop() should be called", mMyObject.isRunning());
}

Но это не работает, я думаю, это потому, что это в обработчике и новом потоке.

Мой тестовый класс расширяет AndroidTestCase.Что я должен делать ?Какова лучшая практика для этого случая?

С уважением.

Ответы [ 2 ]

9 голосов
/ 11 октября 2011

Когда я имею дело с тестированием многопоточного кода, я стараюсь, чтобы программа выполняла как можно большую часть своего естественного потока. Кроме того, я избегаю использования операторов сна, поскольку вы не получаете никаких гарантий того, что выбранный вами интервал сна достаточен для того, чтобы субъект вашего теста мог завершить свою работу; вам часто приходится выбирать слишком большие интервалы сна, что приводит к гораздо более медленному выполнению ваших тестовых случаев.

Я бы порекомендовал вам попробовать добавить некоторый код в класс, который вы тестируете, в данном случае MyObject, который вызывает слушатель всякий раз, когда что-то происходит. Похоже, что у вас уже есть методы обратного вызова для onStart() и onStop() (если это события / обратные вызовы), поэтому они должны вызываться, и вы должны использовать их для управления потоком вашего теста. Когда вы получаете событие onStart(), вам следует позвонить stop() и дождаться события onStop().

Обновление

Прежде всего, у вас есть избыточный код:

public void start() {
    final Handler onStartHandler = new Handler();
    new Thread() {
        @Override
        public void run() {
            super.run();

            onStartHandler.post(new Runnable() {
                @Override
                public void run() {
                    mIsRunning = true;
                    onStart();
                }
            });
        }
    }.start();
}

Либо запустите новый поток для вызова onStart(), либо запланируйте выполнение в очереди потоков обработчика.

Версия 1 - удалите обработчик и просто позвольте коду выполнить в новом потоке:

public void start() {
    new Thread() {
        @Override
        public void run() {
            super.run();
            mIsRunning = true;
            onStart();
        }
    }.start();
}

Версия 2 - использовать обработчик только для асинхронного выполнения обратного вызова:

public void start() {
    final Handler onStartHandler = new Handler();

    onStartHandler.post(new Runnable() {
        @Override
        public void run() {
            mIsRunning = true;
            onStart();
        }
    });
}

И второе: я заметил, что если у вас нет Looper , то все, что вы публикуете с Handler, будет игнорироваться (поэтому оно никогда не будет вызываться). Для получения дополнительной информации о шаблоне Looper-Handler см. Статью: Android Guts: введение в Loopers и Handlers . Looper и Handler должны быть присоединены к одной и той же нити (обычно основной нити). Кроме того, если вы создаете Handler в отдельном потоке, как ваш Looper, то вы столкнетесь с той же проблемой: все, что вы публикуете с Handler, будет игнорироваться.

Вот еще несколько хороших вопросов и статей о луперах и обработчиках:

Взаимосвязь между Looper, Handler и MessageQueue показана ниже: enter image description here

0 голосов
/ 11 октября 2011

Проблема в том, что вы вызываете onStart (), который вызывает новый поток, а затем сразу же спрашивает, запущен ли он. Есть время запуска для нового потока, и пока это происходит, ваш тест спрашивает, запущен ли он - это не ДА.

Бьюсь об заклад, если вы ждали, используя Thread.sleep () или цикл, вы обнаружите, что он запущен "в конце концов".

Что вы на самом деле пытаетесь проверить?

Если вам нужен новый поток, вы можете прочитать о потоках, синхронизировать и т. Д. http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html

...