2010-10-16 30 views
18

Sto lavorando a un'app Android in cui è possibile scaricare i dati JSON da un servizio Web. La classe di analisi dei dati simile a questa:Come test unitario JSON parsing

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; 
    } 
} 

Si costruisce bene e funziona quando chiamo parseCourse() da dentro il mio app, ma quando provo a testare questo metodo in una prova di unità ottengo questa eccezione:

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 

sembra che le classi del pacchetto org.json che vengono con il quadro di Android non sono disponibili in Java per impostazione predefinita.

C'è un modo per risolvere il problema in modo da poter testare le classi che analizzano JSON?

risposta

46

Tutto quello che dovete fare è aggiungere la seguente riga alla sezione dipendenza nel tuo build.gradle:

testCompile 'org.json:json:20140107' 

Si noti che è anche è necessario utilizzare Android Studio 1.1 o versione successiva e almeno creare gli strumenti versione 22.0.0 o superiore affinché funzioni!

testCompile significa che la dipendenza è inclusa poiché una dipendenza di test verrà utilizzata solo quando il progetto viene compilato per l'esecuzione dei test di unità. Il vaso fornito non verrà incluso nel tuo APK e verrà utilizzato solo per sostituire le classi mancanti nel pacchetto org.json.

+0

Grazie per questo Xaver! Stavo cominciando a pensare che nessun altro stia provando a usare Test unitari con gli ultimi strumenti di costruzione. –

19

Edit: si veda la risposta aggiornata alla fine

ho trovato la risposta a questo. Non risolve il mio problema, ma almeno spiega perché c'è un problema.

In primo luogo, ho assunto l'ipotesi erronea che il JSONObject (importato dal pacchetto org.json) fosse incluso come parte di JRE. Non è - in un progetto Android, questo si trova in android.jar (classico momento "duh").

Questa scoperta ha aumentato un po 'la mia autostima. Questo potrebbe facilmente essere risolto aggiungendo un riferimento allo android.jar nel mio progetto di test di unità - o almeno così ho pensato per un breve momento. In questo modo solo mi ha dato un altro errore quando si esegue il mio test:

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

Almeno questo mi ha dato qualcosa di più a Google per. Quello che ho trovato tuttavia, non è stato davvero incoraggiante (ancora un altro classico momento "duh") ...

Questo blog descrive abbastanza bene il problema: Why Android isn’t ready for TDD, and how I tried anyway. Se non si preoccupano di leggere il tutto, la breve spiegazione è la seguente:

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.

Quando ulteriormente fare un po 'googling con questo in mente, ho trovato diversi articoli, blog e anche questioni qui per quanto riguarda il SO problema. Aggiungerò alcuni link qui alla fine, per quelle che potrebbero essere alla ricerca:

E ce ne sono molti altri se si guarda intorno.

Ci sono diversi suggerimenti/soluzione/approcci alternativi alla sperimentazione unità in Android in molti di questi collegamenti, ma non mi preoccupai di cercare di fare una buona risposta in base a che (in quanto v'è, ovviamente, modo troppo Ancora non conosco lo sviluppo di Android).Se qualcuno ha qualche belle punte, però, sarò felice di sentire su di loro :)

UPDATE:
Dopo aver sperimentato un po 'di più, in realtà ho riuscito a trovare una soluzione di lavoro a questo problema specifico. Il motivo per cui non l'ho provato in primo luogo, è che pensavo di aver letto da qualche parte che sarebbe problematico includere le "normali" librerie java nella mia app per Android. Stavo provando tante cose diverse per aggirare il mio problema, quindi ho pensato di provare anche io - e in realtà ha funzionato! Ecco come:

  • ho scaricato la fonte per la "vera" org.json pacchetto da qui: http://www.json.org/java/index.html
  • Poi ho compilato il codice sorgente e imballato insieme nel mio json.jar
  • I aggiunto di recente creazione json.jar al percorso di generazione del mio progetto (il progetto principale della mia applicazione Android, non il progetto di test)

Nessuna modifica al mio codice, nessuna modifica al mio test, solo aggiungendo questa libreria. E tutto funziona, sia la mia app per Android che i miei test di unità.

ho testato anche passando attraverso il codice in modalità debug, e durante il debug di Android App del JSONObject nel mio JsonCourseParser prelevato dalla SDK di Android (android.jar), ma quando il debug il mio test di unità che vengono recuperati da il mio json.jar. Non sono sicuro se ciò significa che lo json.jar non è incluso quando la mia app è costruita, o se il runtime seleziona in modo intelligente la libreria corretta da utilizzare. Inoltre non sono sicuro che questa libreria aggiuntiva possa avere altri effetti sulla mia app finale. Immagino che solo il tempo lo dirà ...

+0

grazie per l'analisi dettagliata e la descrizione del problema! E il tempo ti ha detto qualcosa per questo problema? – paweloque

+0

Costruire il vaso della sorgente ha funzionato per me! Sembra davvero sciocco che Android non includa questo nell'SDK da qualche parte – lifeson106

0

Ho avuto lo stesso problema ma ho trovato un modo migliore. Il framework Roboelectric fa il lavoro. All'inizio ho appena aggiunto roboelectric.jar come libreria esterna (perché non utilizzo Maven) al percorso di build del mio progetto di test Java JUnit e come "annotazione di classe" ho aggiunto @RunWith(RobolectricTestRunner.class). Ma poi ho ottenuto un NoClassDefFoundError: android/net/Uri. Dopo ho aggiunto ancora lo stesso android.jar usato dal progetto Android corrispondente (anche se la libreria Android con Android.jar era già una parte del mio percorso di build), funziona.

-1

Ho avuto questo problema esatto nei miei test JSON. La tua risposta aggiornata mi ha portato a provare un approccio più semplice, che ha funzionato per il mio progetto. Mi permette di eseguire test JUnit, utilizzando la 'vera' JAR org.json, sulle mie classi non-Android dall'interno di Eclipse:

  1. Scarica json.jar di org.json da qui (o dovunque): http://mvnrepository.com/artifact/org.json/json
  2. Aggiungere una cartella 'libs-test' al progetto, copiare il file json.jar ad esso
  3. In Eclipse aperto: Run/Run Configurazioni, selezionare JUnit/YourTestClass
  4. Nella scheda Classpath, rimuovere " API di Google "da voci Bootstrap
  5. Fare clic su" Aggiungi JAR ", vai a /libs-test/json.jar e aggiungilo.
  6. Fare clic su "Chiudi"
  7. Eseguire i test
2

Uso il plug-in Gradle di Björn Quentin Unmock, che consente di sostituire le classi stubbed nel android.jar predefinito con le versioni reali da un altro android.jar (ad esempio Robolectric).

+0

'keepStartingWith" org.json. "' –