Как выполнить модульный тест парсинга JSON - PullRequest
21 голосов
/ 17 октября 2010

Я работаю над приложением для 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() из моего приложения, но когда я пытаюсь проверить этот метод в модульном тесте, яполучите это исключение:

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.

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

Ответы [ 5 ]

48 голосов
/ 16 апреля 2015

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

testImplementation 'org.json:json:20140107'

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

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

19 голосов
/ 20 октября 2010

Редактировать: см. Обновленный ответ в конце

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

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

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

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

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

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

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

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

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

Во многих из этих ссылок есть несколько предложений / решений / альтернативных подходов к модульному тестированию в 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 ), но при отладке мой модульный тест получен из моего json.jar . Не уверен, означает ли это, что json.jar не включается при сборке моего приложения или если среда выполнения разумно выбирает правильную библиотеку для использования. Также не уверен, может ли эта дополнительная библиотека повлиять на мое последнее приложение. Я думаю, только время покажет ...

2 голосов
/ 03 июня 2015

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

0 голосов
/ 04 мая 2013

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

  1. Загрузите файл json.jar из org.json отсюда (или где-либо еще): http://mvnrepository.com/artifact/org.json/json
  2. Добавьте папку 'libs-test' в свой проект, скопируйте в нее файл json.jar
  3. В Eclipse open: Run / Run Configurations выберите JUnit / YourTestClass
  4. На вкладке Classpath удалите «API Google» из записей начальной загрузки
  5. Нажмите «Добавить JAR», перейдите к /libs-test/json.jar и добавьте его.
  6. Нажмите «Закрыть»
  7. Запустите тесты
0 голосов
/ 08 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...