Тестирование кода 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>
.
Ответ 4
unmock-plugin может помочь https://github.com/bjoernQ/unmock-plugin. В моем случае он работает с SparseArray.
Ответ 5
Не будет встроенных тестов android/junit делать то, что вы хотите?