Подтвердить что ты не робот

Как unit test Разбор JSON

Я работаю над Android-приложением, в котором загружаю данные JSON из веб-службы. Класс, анализирующий данные, выглядит примерно так:

public class JsonCourseParser implements CourseParser {

    public Course parseCourse(String courseData) {
        Course result;

        try {
            JSONObject jsonObject = new JSONObject(courseData);

            ...

            result = new Course("foo", "bar");
        } catch (JSONException e) {
            result = null;
        }

        return result;
    }
}

Это просто отлично, и он работает, когда я вызываю parseCourse() из моего приложения, но когда я пытаюсь проверить этот метод в unit test, я получаю это исключение:

java.lang.NoClassDefFoundError: org/json/JSONException
    at JsonCourseParserTest.initClass(JsonCourseParserTest.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.ClassNotFoundException: org.json.JSONException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
    ... 16 more

Кажется, что классы пакета org.json, поставляемые с Android Framework, по умолчанию недоступны в Java.

Есть ли способ исправить это, чтобы я мог unit test классы, которые анализируют JSON?

4b9b3361

Ответ 1

Все, что вам нужно сделать, это добавить следующую строку в раздел зависимости в файле build.gradle:

testCompile 'org.json:json:20140107'

Обратите внимание, что вам также нужно использовать Android Studio 1.1 или выше и, по крайней мере, создавать версии инструментов 22.0.0 или выше для этого!

testCompile означает, что зависимость включена как тестовая зависимость, будет использоваться только тогда, когда ваш проект компилируется для выполнения модульных тестов. Поставляемая банка не будет включена в ваш APK и будет использоваться только для замены отсутствующих классов в пакете org.json.

Ответ 2

Изменить: см. обновленный ответ в конце

Я нашел ответ на этот вопрос. Это не решает мою проблему, но, по крайней мере, это объясняет, почему возникает проблема.

Во-первых, я сделал ошибочное предположение о том, что JSONObject (импортированный из пакета org.json) был включен как часть JRE. Это не так - в Android-проекте это находится в android.jar (классический момент "duh" ).

Это открытие несколько увеличило мою уверенность в себе. Это можно было легко решить, добавив ссылку на android.jar в мой проект unit test - или, по крайней мере, так я подумал на короткое время. Это дало мне еще одну ошибку при выполнении моего теста:

java.lang.RuntimeException: Stub!
    at org.json.JSONObject.<init>(JSONObject.java:8)
    ...

По крайней мере, это дало мне еще кое-что для Google. То, что я нашел, однако, было не очень обнадеживающим (еще один классический момент "duh" )...

Этот блог описывает проблему довольно хорошо: Почему Android не готов для TDD, и как я все-таки пробовал. Если вы не утруждаете себя чтением всего, краткое объяснение выглядит следующим образом:

Проблема заключается в том, что android.jar, поставляемый с SDK, является вырезано без реализации код. Решение, которое, как представляется, ожидается, что вы должны запустить свой модульные тесты на эмуляторе или реальные телефон.

Когда мы больше занимались поиском в Google, я нашел несколько статей, блогов, а также вопросы здесь по поводу этой проблемы. Я добавлю несколько ссылок здесь в конце, для тех, которые могут выглядеть:

И есть еще много, если вы посмотрите вокруг.

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

UPDATE:
После экспериментов немного больше, мне действительно удалось найти рабочее решение для этой конкретной проблемы. Причина, по которой я не пробовал это в первую очередь, заключалась в том, что я думал, что где-то читал, что было бы проблематично включать "обычные" java-библиотеки в мое приложение для Android. Я пробовал так много разных вещей, чтобы обойти мою проблему, поэтому я подумал, что просто попробую попробовать, и это действительно сработало! Вот как:

  • Я загрузил источник для "реального" org.json пакета отсюда: http://www.json.org/java/index.html
  • Затем я скомпилировал исходный код и упаковал его в свой собственный json.jar
  • Я добавил вновь созданный json.jar к пути сборки моего проекта (основного проекта моего приложения для Android, а не тестового проекта).

Никаких изменений в моем коде, никаких изменений в моем тесте, только добавление этой библиотеки. И все работает, как мое приложение для Android, так и мои модульные тесты.

Я также тестировал переход через код в режиме отладки, а при отладке приложения Android JSONObject в моем JsonCourseParser извлекается из Android SDK (android.jar), но при отладке моего unit test это из моего собственного json.jar. Не уверен, что это означает, что json.jar не включен, когда мое приложение построено, или если среда выполнения разумно выбирает нужную библиотеку для использования. Также не уверен, что эта дополнительная библиотека может иметь какие-либо другие эффекты для моего последнего приложения. Думаю, только время покажет...

Ответ 3

Я использую Björn Quentin Unmock Gradle плагин, который позволяет вам заменять заостренные классы в файле android.jar по умолчанию реальные версии от другого android.jar(например, Robolectric).

Ответ 4

У меня была такая же проблема, но я нашел лучший способ. Рамка Roboelectric выполняет свою работу. Сначала я просто добавил roboelectric.jar в качестве внешней библиотеки (потому что я не использую Maven) для пути сборки моего проекта Java JUnit и в качестве "аннотации класса" я добавил @RunWith(RobolectricTestRunner.class). Но затем я получил NoClassDefFoundError: android/net/Uri. После того, как я по-прежнему добавил сам android.jar, который использовался в соответствующем проекте Android (хотя библиотека Android с ее андроид .jar уже была частью моего пути сборки), она работает.

Ответ 5

У меня была эта точная проблема в тестах JSON. Ваш обновленный ответ заставил меня попробовать более простой подход, который работал для моего проекта. Это позволяет мне запускать тесты JUnit, используя "real" org.json JAR, на моих не-Android-классах из Eclipse:

  • Загрузите org.json json.jar отсюда (или где угодно): http://mvnrepository.com/artifact/org.json/json
  • Добавьте в проект папку "libs-test", скопируйте файл json.jar в него
  • В Eclipse open: Run/Run Configurations, выберите JUnit/YourTestClass
  • На вкладке "Класс" удалите "API Google" из записей Bootstrap
  • Нажмите "Добавить JAR", перейдите в /libs -test/json.jar и добавьте его.
  • Нажмите "Закрыть"
  • Запустите тесты