Posso usare assert sui dispositivi Android?


Risposte:


-7

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.


51
Ebbene, l'OP ha chiesto “assert keyword” che, a differenza di junit.framework.Assert, può essere ottimizzato dal JIT. E proprio per questo sono venuto qui. Spero che alcune delle altre risposte siano più utili.
Martin

27
Odio essere scortese, ma questa non dovrebbe essere la risposta accettata perché non risponde alla domanda (sono d'accordo con il commento di @Martin). Altre risposte spiegano come far funzionare correttamente la parola chiave assert, ad es. Esegui "adb shell setprop debug.assert 1"
jfritz42

3
Downvoted perché OP ha chiesto di asserire la parola chiave. @scorpiodawg ha delineato processo qui di seguito: stackoverflow.com/a/5563637/484261

La risposta di scorpiodawg è arrivata solo un anno dopo, quindi immagino che questa risposta sia stata accettata solo perché l'OP, per qualche motivo, si è sentito obbligato a contrassegnare una risposta come accettata. Questo atteggiamento rende un numero enorme di risposte su SO incomplete o addirittura terribili.
asincrono

145

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.


Sembra che Google abbia rimosso la fonte sfogliabile (ho visto riferimenti a questo nelle risposte ad altre domande qui). La soluzione migliore è ottenere la fonte o tentare di cercarla in un motore di ricerca. Cerca "dalvik / embedded-vm-control.html". Ecco un posto che ce l'ha: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/… . Spero che questo ti aiuti.
scorpiodawg

Molto utile, grazie. Tuttavia, sono confuso dalla distinzione fatta nel penultimo paragrafo. Stiamo discutendo due tipi di asserzioni: il primo riguarda i metodi del pacchetto JUnit Assert come assertNotNull () e il secondo è la parola chiave 'assert' del linguaggio Java. La tua risposta si applica a entrambi? Ad esempio, se io 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?
Jeffro

1
Ciao Jeffro, non credo proprio: junit.framework.Assert è semplicemente una classe che genera un'eccezione quando la condizione di input risulta falsa. La parola chiave assert, d'altra parte, è incorporata nella lingua. Spero che questo ti aiuti.
scorpiodawg

3
C'è un modo per fare adb shell setprop debug.assert 1in Eclipse?
Pacerier

1
Le asserzioni possono essere fatte anche da un terminale in esecuzione sul dispositivo se sei root. suAllora 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.
ahcox

10

Quando le asserzioni sono abilitate, la assertparola chiave genera semplicemente un AssertionErrorquando l'espressione booleana è false.

Quindi IMO, la migliore alternativa, esp. se sei contrario a dipendere da junit, è di lanciare un AssertionErroresplicitamente 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 AssertionErrorcome alternativa accettabile.

Immagino che tu possa configurare Proguard per eliminare queste chiamate per il codice di produzione.


Ma come si abilitano le asserzioni? Su Android Studio?
SMBiggs

8

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

Per stackoverflow.com/a/18556839/2004714 , potresti anche dover assicurarti che il file sia di sola lettura ( chmod 644).
Paulo

5

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 (...);


4

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.


1
Penso che la sintassi corretta sarebbe:-assumenosideeffects class junit.framework.Assert { *; }
Pooks

Risposta confusa. Il comando menzionato causa un errore di proguard. Il comando corretto da Pooks continua a non rimuovere le asserzioni dal file dex binario.
Pointer Null

Ho lo stesso problema @PointerNull. asserire non verrà rimosso.
Mahdi

3

È possibile utilizzare le asserzioni, ma è necessario un po 'di lavoro per usarle in modo affidabile. La proprietà del sistema debug.assertnon è affidabile; vedere i problemi 175697 , 65183 , 36786 e 17324 .

Un metodo consiste nel tradurre ogni assertistruzione 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 ifun'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 .


1

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.


0

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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.