Desidero utilizzare la parola chiave Assert nelle mie app Android per distruggere la mia app in alcuni casi sull'emulatore o il mio dispositivo durante il test. È possibile?
Sembra che l'emulatore ignori le mie affermazioni.
Desidero utilizzare la parola chiave Assert nelle mie app Android per distruggere la mia app in alcuni casi sull'emulatore o il mio dispositivo durante il test. È possibile?
Sembra che l'emulatore ignori le mie affermazioni.
Risposte:
L'API fornisce JUnit Assert .
Tu puoi fare
import static junit.framework.Assert.*;
ora puoi usare tutte le funzioni come assertTrue, assertEquals, assertNull fornite nel framework junit.
Fare attenzione a non importare il framework Junit4 tramite eclipse, che sarebbe il pacchetto org.junit. Devi usare il pacchetto junit.framework per farlo funzionare su un dispositivo Android o l'emulatore.
Vedere il documento Embedded VM Control (HTML grezzo dall'albero dei sorgenti o una copia ben formattata ).
Fondamentalmente, la Dalvik VM è impostata per ignorare i controlli di asserzione per impostazione predefinita, anche se il codice byte .dex include il codice per eseguire il controllo. Il controllo delle asserzioni viene attivato in uno dei due modi seguenti:
(1) impostando la proprietà di sistema "debug.assert" tramite:
adb shell setprop debug.assert 1
che ho verificato funziona come previsto fintanto che reinstalli la tua app dopo averlo fatto, o
(2) inviando l'argomento della riga di comando "--enable-assert" alla VM di Dalvik che potrebbe non essere qualcosa che gli sviluppatori di app probabilmente saranno in grado di fare (qualcuno mi corregga se sbaglio qui).
Fondamentalmente, esiste un flag che può essere impostato globalmente, a livello di pacchetto o a livello di classe che abilita le asserzioni a quel rispettivo livello. Il flag è disattivato per impostazione predefinita, in conseguenza del quale vengono saltati i controlli di asserzione.
Ho scritto il codice seguente nella mia attività di esempio:
public class AssertActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int x = 2 + 3;
assert x == 4;
}
}
Per questo codice, il codice byte dalvik generato è (per Android 2.3.3):
// Static constructor for the class
000318: |[000318] com.example.asserttest.AssertActivity.:()V
000328: 1c00 0300 |0000: const-class v0, Lcom/example/asserttest/AssertActivity; // class@0003
00032c: 6e10 0c00 0000 |0002: invoke-virtual {v0}, Ljava/lang/Class;.desiredAssertionStatus:()Z // method@000c
000332: 0a00 |0005: move-result v0
000334: 3900 0600 |0006: if-nez v0, 000c // +0006
000338: 1210 |0008: const/4 v0, #int 1 // #1
00033a: 6a00 0000 |0009: sput-boolean v0, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000
00033e: 0e00 |000b: return-void
000340: 1200 |000c: const/4 v0, #int 0 // #0
000342: 28fc |000d: goto 0009 // -0004
:
:
// onCreate()
00035c: |[00035c] com.example.asserttest.AssertActivity.onCreate:(Landroid/os/Bundle;)V
00036c: 6f20 0100 3200 |0000: invoke-super {v2, v3}, Landroid/app/Activity;.onCreate:(Landroid/os/Bundle;)V // method@0001
000372: 1501 037f |0003: const/high16 v1, #int 2130903040 // #7f03
000376: 6e20 0500 1200 |0005: invoke-virtual {v2, v1}, Lcom/example/asserttest/AssertActivity;.setContentView:(I)V // method@0005
00037c: 1250 |0008: const/4 v0, #int 5 // #5
00037e: 6301 0000 |0009: sget-boolean v1, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000
000382: 3901 0b00 |000b: if-nez v1, 0016 // +000b
000386: 1251 |000d: const/4 v1, #int 5 // #5
000388: 3210 0800 |000e: if-eq v0, v1, 0016 // +0008
00038c: 2201 0c00 |0010: new-instance v1, Ljava/lang/AssertionError; // class@000c
000390: 7010 0b00 0100 |0012: invoke-direct {v1}, Ljava/lang/AssertionError;.:()V // method@000b
000396: 2701 |0015: throw v1
000398: 0e00 |0016: return-void
Notare come il costruttore statico richiama il metodo desideratoAssertionStatus sull'oggetto Class e imposta la variabile a livello di classe $ assertionsDisabled; si noti anche che in onCreate (), tutto il codice per lanciare java.lang.AssertionError è compilato, ma la sua esecuzione è subordinata al valore di $ assertionsDisabled che è impostato per l'oggetto Class nel costruttore statico.
Sembra che la classe Assert di JUnit sia quella utilizzata prevalentemente, quindi è probabile che sia una scommessa sicura da usare. La flessibilità della parola chiave assert è la capacità di attivare le asserzioni in fase di sviluppo e disattivarle per i bit di spedizione e invece fallire regolarmente.
Spero che questo ti aiuti.
import static junit.framework.Assert.*
e poi utilizziamo uno dei suoi metodi, ad esempio assertNotNull("It's null!", someObject);
, questa affermazione è disattivata nei bit di spedizione?
adb shell setprop debug.assert 1
in Eclipse?
su
Allora prima setprop debug.assert 1
. Tieni presente che il codice che mostri disassemblato rimarrà in una build di rilascio ( stackoverflow.com/a/5590378/506073 ). Non credo che al compilatore javac si possa dire di non emettere asserzioni, quindi devono essere rimosse in qualche modo. Una soluzione semplice è racchiudere la parola chiave assert nella tua funzione che proguard può rimuovere per te.
Quando le asserzioni sono abilitate, la assert
parola chiave genera semplicemente un AssertionError
quando l'espressione booleana è false
.
Quindi IMO, la migliore alternativa, esp. se sei contrario a dipendere da junit, è di lanciare un AssertionError
esplicitamente come mostrato di seguito:
assert x == 0 : "x = " + x;
Un'alternativa alla dichiarazione di cui sopra è:
Utils._assert(x == 0, "x = " + x);
Dove il metodo è definito come:
public static void _assert(boolean condition, String message) {
if (!condition) {
throw new AssertionError(message);
}
}
I documenti Oracle Java consigliano di lanciare un AssertionError
come alternativa accettabile.
Immagino che tu possa configurare Proguard per eliminare queste chiamate per il codice di produzione.
In "Android in Practice" si suggerisce di utilizzare:
$adb shell setprop dalvik.vm.enableassertions all
se queste impostazioni non sono persistenti sul tuo telefono, puoi creare il file /data/local.prop con proprietà come:
dalvik.vm.enableassertions=all
chmod 644
).
Mi stava infastidendo a morte che le mie affermazioni non funzionavano, fino a quando non ho verificato il problema su Google ... Ho rinunciato a semplici asserzioni e andrò con i metodi di asserzione Junits.
Per praticità sto usando:
import static junit.framework.Assert. *;
A causa dell'importazione statica posso successivamente scrivere:
assertTrue (...); invece di Assert.assertTrue (...);
Se sei preoccupato per il codice di spedizione con l'asserzione JUnit in (o qualsiasi altro percorso di classe), puoi utilizzare l'opzione di configurazione di ProGuard 'assumenosideeffects', che eliminerà un percorso di classe supponendo che rimuoverlo non influisca sul codice .
Per esempio.
-assumenosideeffects junit.framework.Assert {
*;
}
Ho una libreria di debug comune in cui inserisco tutti i miei metodi di test e quindi uso questa opzione per rimuoverla dalle mie app rilasciate.
Ciò elimina anche il problema difficile da individuare delle stringhe manipolate che non vengono mai utilizzate nel codice di rilascio. Ad esempio, se si scrive un metodo di registro di debug e in quel metodo si verifica la modalità di debug prima di registrare la stringa, si sta ancora costruendo la stringa, allocando memoria, chiamando il metodo, ma poi optando per non fare nulla. Rimuovere la classe quindi rimuove completamente le chiamate, il che significa che finché la stringa è costruita all'interno della chiamata al metodo, anche essa scompare.
Assicurati che sia davvero sicuro togliere semplicemente le linee, poiché viene fatto senza alcun controllo da parte di ProGuard. La rimozione di qualsiasi metodo di restituzione del vuoto andrà bene, tuttavia se stai prendendo dei valori di ritorno da qualunque cosa stai rimuovendo, assicurati di non usarli per la logica operativa effettiva.
-assumenosideeffects class junit.framework.Assert { *; }
È possibile utilizzare le asserzioni, ma è necessario un po 'di lavoro per usarle in modo affidabile. La proprietà del sistema debug.assert
non è affidabile; vedere i problemi 175697 , 65183 , 36786 e 17324 .
Un metodo consiste nel tradurre ogni assert
istruzione in qualcosa con cui qualsiasi runtime può gestire. Fallo con un preprocessore sorgente davanti al compilatore Java. Ad esempio, prendi questa affermazione:
assert x == 0: "Failure message";
Per una build di debug, il tuo preprocessore tradurrebbe quanto sopra in if
un'istruzione:
{ if( !(x == 0) ) throw new AssertionError( "Failure message" ); }
Per una build di produzione, a un'istruzione vuota:
;
Si noti che questo controllerebbe le asserzioni in fase di compilazione, al contrario del tempo di esecuzione (la pratica abituale).
Non sono riuscito a trovare alcun preprocessore già pronto, quindi ne ho creato uno script . Vedere la parte relativa alle asserzioni. La licenza per la copia è qui .
Da aggiungere alla risposta di Zulaxia sulla rimozione di Junit - Proguard fa già parte di Android SDK / Eclipse e la pagina seguente ti dice come abilitarlo.
http://developer.android.com/guide/developing/tools/proguard.html
Anche quanto sopra non funzionerà con l'ultima configurazione predefinita di proguard perché utilizza il flag -dontoptimize che deve essere rimosso e alcune delle ottimizzazioni attivate.
Utilizza la parola chiave di asserzione Java standard , ad esempio:
assert a==b;
Perché funzioni, devi aggiungere una riga a /system/build.prop e riavviare il telefono:
debug.assert=1
Questo funzionerebbe su un telefono con root. Usa un file manager in grado di modificare build.prop (ad esempio X-plore).
Vantaggi: la maggior parte (tutti?) Dei telefoni Android vengono forniti con le asserzioni disabilitate. Anche se il tuo codice si afferma accidentalmente come falso, l'app non si interromperà o si bloccherà. Tuttavia, sul tuo dispositivo di sviluppo otterrai un'eccezione di asserzione.