2010-10-16 18 views
18

Estoy trabajando en una aplicación de Android en la que descargo datos JSON de un servicio web. La clase de análisis de los datos se ve algo como esto:Cómo probar la unidad de análisis 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; 
    } 
} 

Esto construye muy bien y funciona cuando llamo parseCourse() desde dentro de mi aplicación, pero cuando intento para probar este método en una prueba de unidad consigo esta excepción:

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 

parece que las clases de la org.json paquete que viene con el Marco de Android no están disponibles en Java por defecto.

¿Hay alguna manera de solucionarlo, así puedo probar las clases que analizan JSON?

Respuesta

46

Todo lo que necesita hacer es añadir la siguiente línea a la sección de la dependencia en su build.gradle:

testCompile 'org.json:json:20140107' 

en cuenta que también necesita usar Android Studio 1.1 o superior y al menos herramientas de compilación versión 22.0.0 o superior para que esto funcione!

testCompile significa que la dependencia se incluye como una dependencia de prueba que solo se utilizará cuando se compile su proyecto para ejecutar pruebas unitarias. El archivo jar suministrado no se incluirá en su APK y solo se usará para reemplazar las clases faltantes en el paquete org.json.

+0

¡Gracias por este Xaver! Estaba empezando a pensar que nadie más estaba tratando de usar Unit Tests con las últimas herramientas de compilación. –

19

Editar: véase la respuesta actualizada al final

que he encontrado la respuesta a esta pregunta. No resuelve mi problema, pero al menos explica por qué hay un problema.

En primer lugar, hice la suposición errónea de que el JSONObject (importado del paquete org.json) se incluyó como parte del JRE. No es - en un proyecto de Android, esto reside en android.jar (momento clásico "duh").

Este descubrimiento aumentó mi autoconfianza un poco. Esto podría resolverse fácilmente agregando una referencia al android.jar en mi proyecto de prueba de unidad, o al menos eso pensé por un breve momento. Si lo hace, sólo me dio otro error al ejecutar mi prueba:

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

Al menos eso me dio algo más a Google para. Lo que encontré, sin embargo, no fue realmente alentador (otro clásico momento "duh") ...

Este blog describe bastante bien el problema: Why Android isn’t ready for TDD, and how I tried anyway. Si no se molestan en leer todo el asunto, la breve explicación es la siguiente:

The problem here is that the android.jar supplied with the SDK is stubbed out with no implementation code. The solution that seems to be expected is that you should run your unit tests on the emulator or a real phone.

Al hacer aún más algunos googlear con esto en mente, he encontrado varios artículos, blogs y también cuestiona aquí en relación con el SO problema. Voy a añadir algunos enlaces aquí, al final, para aquellos que podrían estar buscando:

Y hay mucho más si miras alrededor.

Hay varias sugerencias/solución/enfoques alternativos para las pruebas unitarias en Android en muchos de esos enlaces, pero no me molesto en tratar de hacer una buena respuesta sobre la base de que (ya que no es, obviamente, demasiado Todavía no sé sobre el desarrollo de Android).Si alguien tiene algún consejo agradables sin embargo, estaremos encantados de oír hablar de ellos :)

ACTUALIZACIÓN:
Después de experimentar un poco más, de hecho me las arreglé para encontrar una solución de trabajo a este problema específico. La razón por la que no intenté esto en primer lugar, fue porque pensé que había leído en algún lado que sería problemático incluir bibliotecas java "normales" en mi aplicación de Android. Estaba intentando tantas cosas diferentes para solucionar mi problema, así que pensé que podría intentarlo también, ¡y realmente funcionó! Aquí es cómo:

  • he descargado la fuente de la "real" org.json paquete desde aquí: http://www.json.org/java/index.html
  • siguiente que compilado el código fuente y se envasan juntos en mi propia json.jar
  • I agregó el recién creado json.jar a la trayectoria de la estructura de mi proyecto (el proyecto principal de mi aplicación para Android, no el proyecto de prueba)

no hay cambios en mi código, no hay cambios en mi prueba, solo agregué esta biblioteca. Y todo funciona, tanto mi aplicación de Android como la de mi unidad.

También he probado paso a paso por el código en modo de depuración, y al depurar el Android App el JSONObject en mi JsonCourseParser es inverosímil desde el SDK de Android (android.jar), pero cuando la depuración de mi unidad de prueba que se obtiene de mi propio json.jar. No estoy seguro de si esto significa que json.jar no está incluido cuando mi aplicación está compilada, o si el tiempo de ejecución selecciona inteligentemente la biblioteca correcta para usar. Además, no estoy seguro de si esta biblioteca adicional podría tener otros efectos en mi aplicación final. Supongo que solo el tiempo dirá ...

+0

gracias por su detallado análisis y descripción del problema! ¿Y el tiempo te dijo algo sobre este problema? – paweloque

+0

¡Crear el jarro desde la fuente funcionó para mí! Parece realmente tonto que Android no incluya esto en el SDK en algún lugar – lifeson106

0

Tuve el mismo problema, pero encontré una mejor manera. El marco Roboelectric hace el trabajo. Al principio acabo de agregar el roboelectric.jar como biblioteca externa (porque no uso Maven) a la ruta de compilación de mi proyecto de prueba JUnit de Java y como "anotación de clase" agregué @RunWith(RobolectricTestRunner.class). Pero luego obtuve un NoClassDefFoundError: android/net/Uri. Después de que todavía agregué el android.jar mismo utilizado por el proyecto correspondiente de Android (aunque la biblioteca de Android con su android.jar ya era parte de mi ruta de compilación), funciona.

-1

Tuve este problema exacto en mis pruebas JSON. Su respuesta actualizada me llevó a intentar un enfoque más simple, que funcionó para mi proyecto. Se me permite ejecutar las pruebas JUnit, utilizando el JAR org.json 'real', en mis clases no Android desde dentro de Eclipse:

  1. Descargar json.jar de org.json de aquí (o donde sea): http://mvnrepository.com/artifact/org.json/json
  2. Añadir una carpeta 'libs-test' a su proyecto, copiar el archivo json.jar a ella
  3. En Eclipse abierta: Ejecutar/Ejecutar configuraciones, seleccione JUnit/YourTestClass
  4. En la ficha de ruta de clases, eliminar " Google APIs "de las entradas Bootstrap
  5. Haga clic en" Agregar JAR " ", vaya a /libs-test/json.jar y añádalo.
  6. Haga clic en "Cerrar"
  7. ejecutar las pruebas
2

Estoy usando el complemento Gradle de Björn Quentin Unmock, que le permite reemplazar clases stubbed en el android.jar predeterminado con versiones reales de otro android.jar (por ejemplo, Robolectric).

+0

'keepStartingWith" org.json. "' –

Cuestiones relacionadas