Тестирование кода Android с помощью JUnit и JDK

Я пишу несколько тестов POJO для своего Android-кода.

Я хочу запускать их локально с JDK (не с Dalvik на эмуляторе) - для скорости, JUnit 4, Mockito и возможностью безголового без устройства - поэтому у меня есть отдельный проект Java в Eclipse.

Если метод, который я тестирую, ссылается на что-либо из Android SDK, например. android.util.Log, тест терпит неудачу - это имеет смысл, потому что android.jar не находится в пути к классам. Чтобы воспроизвести, у меня есть этот тестовый пример:

public class FooTests {
  @Test
  public void testFoo() {
    android.util.Log.d("x", "y");
  }
}

Если я добавлю android.jar явно к траектории класса тестового проекта, я получаю исключения, такие как

java.lang.RuntimeException: Stub!
  at android.util.Log.d(Log.java:7)
  at com.example.FooTests.testFoo(FooTests.java:39)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  ...
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Есть ли способ заставить код работать без издевательства со всеми последними битками Android SDK? Возможно, уже существует издеваемое android.jar?

EDIT. На данный момент я закончил обертку классов типа android.util.Log и ввел их в экземпляры для классического тестирования на основе IOC. Предложение Scott PowerMock - это мой следующий шаг, когда он мне понадобится.

LATER EDIT: Robolectric!

Ответ 1

Там нет издевательства android.jar, о котором я знаю. Я использую Powermock, чтобы издеваться над всем. Одна вещь, которую вы можете сделать, чтобы помочь с тяжелым насмешкой, - сделать ваши расширенные классы Android, такие как активность, фрагменты, вещатели и т.д., Тонкой и передать их классу pojo. Вы можете принять решение, основанное на риске, не изолировать unit test расширенные классы android, а вместо этого интегрировать unit test их через платформу тестирования android или что-то вроде Robotium.

Для тестирования подлинности изоляции в Android, для меня модульное тестирование java jvm издевается над всеми сопутствующими классами - лучший способ.

Ответ 2

У меня была та же проблема. Я хотел протестировать простые POJO локально.
В частности, мой код хотел использовать android.util.Base64.
То, что я закончил, - это использовать SDK для установки источников Android 4 и скопировать класс android.util.Base64 в мой проект.
Удивительно, но это сработало.

Ответ 3

У меня была та же проблема. Я хотел протестировать простую бизнес-логику локально. В частности, мой код использует android.util.SparseArray<E>.

Я попробовал 3 разных подхода, чтобы проверить его с помощью junit вне android.

  • В моей бизнес-логике я создал интерфейс и привязал его к MySparseArray<E>, который наследует от SparseArray<E>. В junit-test я повторно реализовал интерфейс, используя HashMap<Integer, E>. Это работает, но в конечном итоге это большая работа, чтобы сделать бизнес-логику единичной, если требуется другое android.*.
  • Как предложил @Tal Weiss, я добавил источники android java для требуемых классов: android.util.SparseArray.java, который использует com.android.internal.util.ArrayUtils.java. Это тоже работает, но я не люблю добавлять дополнительные источники для Android, особенно если есть гораздо больше зависимостей.
  • Я загрузил двоичный файл без заголовка android.jar для старого Android 2.2 из http://grepcode.com/snapshot/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1 и включил его как lib в junit-java -проект. Эта банка содержит только классы для пространств имен android.*, com.android.*, dalvik.* и framework.base.*. Это тоже работает.

В настоящее время мне удалось избежать использования android.* классов, кроме SparseArray<E> в моем бизнес-уровне, и не имеет зависимости от контекста, активности или службы. Я мог бы использовать HashMap<Integer, E> в слое android-business вместо SparseArray<E>.