Kann ich assert auf Android-Geräten verwenden?

88

Ich möchte das Schlüsselwort Assert in meinen Android-Apps verwenden, um meine App in einigen Fällen auf dem Emulator oder meinem Gerät während des Tests zu zerstören. Ist das möglich?

Es scheint, dass der Emulator meine Behauptungen einfach ignoriert.

Janusz
quelle
2
Für ART, anstatt Dalvik, siehe stackoverflow.com/questions/35997703/…
fadden
Beachten Sie, dass die akzeptierte Antwort ziemlich irreführend ist.
Reduzierung der Aktivität

Antworten:

-7

Die API stellt den JUnit Assert bereit .

Du kannst tun

import static junit.framework.Assert.*;

Jetzt können Sie alle Funktionen wie assertTrue, assertEquals und assertNull verwenden, die im junit-Framework bereitgestellt werden.

Achten Sie darauf, das Junit4-Framework nicht über Eclipse zu importieren. Dies ist das Paket org.junit. Sie müssen das Paket junit.framework verwenden, damit es auf einem Android-Gerät oder dem Emulator funktioniert.

JRL
quelle
51
Nun, das OP fragte nach "assert keyword", das - anders als junit.framework.Assert - von der JIT optimiert werden kann. Und genau aus diesem Grund bin ich hierher gekommen. Hoffe, einige der anderen Antworten werden hilfreicher sein.
Martin
27
Ich hasse es, unhöflich zu sein, aber dies sollte nicht die akzeptierte Antwort sein, da sie die Frage nicht beantwortet (ich stimme dem Kommentar von @Martin zu). Andere Antworten erklären, wie das Schlüsselwort assert ordnungsgemäß funktioniert, z. B. "adb shell setprop debug.assert 1"
ausführen
3
Downvoted, weil OP nach dem Schlüsselwort assert gefragt hat. @scorpiodawg hat Prozess unten beschrieben: stackoverflow.com/a/5563637/484261
Die Antwort von scorpiodawg kam nur ein Jahr später, daher wurde diese Antwort vermutlich nur akzeptiert, weil sich das OP aus irgendeinem Grund verpflichtet fühlte, eine Antwort als akzeptiert zu markieren. Diese Einstellung macht eine Vielzahl von Antworten auf SO entweder unvollständig oder geradezu schrecklich.
Async
145

Weitere Informationen finden Sie im Dokument Embedded VM Control (unformatiertes HTML aus dem Quellbaum oder eine gut formatierte Kopie).

Grundsätzlich ist die Dalvik-VM so eingestellt, dass Assertionsprüfungen standardmäßig ignoriert werden, obwohl der .dex-Bytecode den Code zur Durchführung der Prüfung enthält. Das Überprüfen von Zusicherungen kann auf zwei Arten aktiviert werden:

(1) durch Setzen der Systemeigenschaft "debug.assert" über:

adb shell setprop debug.assert 1

was ich überprüft habe, funktioniert wie beabsichtigt, solange Sie Ihre App danach neu installieren, oder

(2) indem Sie das Befehlszeilenargument "--enable-assert" an die dalvik VM senden, was App-Entwickler wahrscheinlich nicht können (jemand korrigiert mich, wenn ich mich hier irre).

Grundsätzlich gibt es ein Flag, das entweder global, auf Paketebene oder auf Klassenebene gesetzt werden kann, um Zusicherungen auf dieser jeweiligen Ebene zu ermöglichen. Das Flag ist standardmäßig deaktiviert, wodurch die Assertionsprüfungen übersprungen werden.

Ich habe den folgenden Code in meine Beispielaktivität geschrieben:


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

Für diesen Code lautet der generierte Dalvik-Byte-Code (für 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

Beachten Sie, wie der statische Konstruktor die gewünschte MethodeAssertionStatus für das Class-Objekt aufruft und die klassenweite Variable $ assertionsDisabled festlegt. Beachten Sie auch, dass in onCreate () der gesamte Code zum Auslösen von java.lang.AssertionError kompiliert wird, seine Ausführung jedoch vom Wert von $ assertionsDisabled abhängt, der für das Class-Objekt im statischen Konstruktor festgelegt ist.

Es scheint, dass die Assert-Klasse von JUnit überwiegend verwendet wird, daher ist es wahrscheinlich eine sichere Wette, dies zu verwenden. Die Flexibilität des Schlüsselworts assert besteht darin, dass Assertions zur Entwicklungszeit aktiviert und für den Versand von Bits deaktiviert werden können und stattdessen ordnungsgemäß fehlschlagen.

Hoffe das hilft.

scorpiodawg
quelle
Es scheint, dass Google die durchsuchbare Quelle entfernt hat (ich habe Hinweise darauf in Antworten auf andere Fragen hier gesehen). Am besten holen Sie sich entweder die Quelle oder versuchen, diese in einer Suchmaschine nachzuschlagen. Suchen Sie nach "dalvik / embedded-vm-control.html". Hier ist ein Ort, der es hat: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/… . Hoffe das hilft.
Scorpiodawg
Sehr nützlich, danke. Ich bin jedoch verwirrt über die Unterscheidung im vorletzten Absatz. Wir diskutieren zwei Arten von Asserts: Das erste sind die Methoden des JUnit Assert-Pakets wie assertNotNull () und das zweite ist das Java-Sprachschlüsselwort 'assert'. Gilt Ihre Antwort für beide? Wenn ich import static junit.framework.Assert.*und dann beispielsweise eine ihrer Methoden verwende assertNotNull("It's null!", someObject);, ist diese Behauptung in Versandbits deaktiviert?
Jeffro
1
Hallo Jeffro, ich glaube nicht - junit.framework.Assert ist einfach eine Klasse, die eine Ausnahme auslöst, wenn festgestellt wird, dass die Eingabebedingung falsch ist. Das assert-Schlüsselwort hingegen ist in die Sprache integriert. Hoffe das hilft.
Scorpiodawg
3
Gibt es eine Möglichkeit, dies adb shell setprop debug.assert 1in Eclipse zu tun ?
Pacerier
1
Zusicherungen können auch von einem Terminal aus erfolgen, das auf dem Gerät ausgeführt wird, wenn Sie root sind. Erst sudann setprop debug.assert 1. Beachten Sie, dass der Code, den Sie zerlegt anzeigen, in einem Release-Build verbleibt ( stackoverflow.com/a/5590378/506073 ). Ich glaube nicht, dass dem Javac-Compiler gesagt werden kann, er solle keine Behauptungen aufstellen, daher müssen sie irgendwie entfernt werden. Eine einfache Lösung besteht darin, das Schlüsselwort assert in Ihre eigene Funktion zu verpacken, die proguard für Sie entfernen kann.
Ahcox
10

Wenn Zusicherungen aktiviert sind, löst das assertSchlüsselwort einfach ein aus, AssertionErrorwenn der boolesche Ausdruck lautet false.

Also IMO, die beste Alternative, insb. Wenn Sie abgeneigt sind, von junit abhängig zu sein, werfen Sie eine AssertionErrorexplizit wie unten gezeigt:

assert x == 0 : "x = " + x;

Eine Alternative zur obigen Aussage ist:

Utils._assert(x == 0, "x = " + x);

Wo die Methode definiert ist als:

public static void _assert(boolean condition, String message) {
    if (!condition) {
        throw new AssertionError(message);
    }
}

In den Oracle Java-Dokumenten wird empfohlen , eine AssertionErrorals akzeptable Alternative zu verwenden.

Ich denke, Sie können Proguard so konfigurieren, dass diese Aufrufe für Produktionscode entfernt werden.

Dheeraj Vepakomma
quelle
Aber wie aktivieren Sie Behauptungen? Auf Android Studio?
SMBiggs
8

In "Android in der Praxis" wird empfohlen, Folgendes zu verwenden:

$adb shell setprop dalvik.vm.enableassertions all

Wenn diese Einstellungen auf Ihrem Telefon nicht beibehalten werden, können Sie die Datei /data/local.prop mit folgenden Eigenschaften erstellen:

dalvik.vm.enableassertions=all
marcinj
quelle
Per stackoverflow.com/a/18556839/2004714 , können Sie auch sicherstellen müssen, dass Datei schreibgeschützt ist ( chmod 644).
Paulo
5

Es hat mich verdammt nervt, dass meine Behauptungen nicht funktionierten, bis ich das Problem bei Google überprüft habe ... Ich habe einfache Behauptungen aufgegeben und werde mich für die Assertionsmethoden von junits entscheiden.

Aus praktischen Gründen verwende ich:

importiere statisches junit.framework.Assert. *;

Aufgrund des statischen Imports kann ich später schreiben:

assertTrue (...); anstelle von Assert.assertTrue (...);

Ready4Android
quelle
4

Wenn Sie Bedenken hinsichtlich des Versandcodes mit den JUnit-Asserts in (oder einem anderen Klassenpfad) haben, können Sie die ProGuard-Konfigurationsoption 'assumenosideeffects' verwenden, mit der ein Klassenpfad unter der Annahme entfernt wird, dass das Entfernen des Codes nichts mit dem Code zu tun hat .

Z.B.

-assumenosideeffects junit.framework.Assert {
*;
}

Ich habe eine gemeinsame Debug-Bibliothek, in die ich alle meine Testmethoden eingefügt habe, und verwende diese Option, um sie aus meinen veröffentlichten Apps zu entfernen.

Dies beseitigt auch das schwer zu erkennende Problem der Manipulation von Zeichenfolgen, die im Release-Code niemals verwendet werden. Wenn Sie beispielsweise eine Debug-Protokollmethode schreiben und bei dieser Methode vor dem Protokollieren der Zeichenfolge nach dem Debug-Modus suchen, erstellen Sie die Zeichenfolge weiterhin, weisen Speicher zu, rufen die Methode auf und entscheiden sich dann dafür, nichts zu tun. Wenn Sie die Klasse entfernen, werden die Aufrufe vollständig entfernt. Solange Ihre Zeichenfolge im Methodenaufruf erstellt wird, wird sie ebenfalls entfernt.

Stellen Sie jedoch sicher, dass es wirklich sicher ist, nur die Leitungen zu entfernen, da dies ohne Überprüfung durch ProGuard erfolgt. Das Entfernen einer ungültigen Rückgabemethode ist in Ordnung. Wenn Sie jedoch Rückgabewerte von dem entfernen, was Sie entfernen, stellen Sie sicher, dass Sie diese nicht für die tatsächliche Betriebslogik verwenden.

Zulaxia
quelle
1
Ich denke, die richtige Syntax wäre:-assumenosideeffects class junit.framework.Assert { *; }
Pooks
Verwirrende Antwort. Der erwähnte Befehl verursacht einen Proguard-Fehler. Der von Pooks korrigierte Befehl entfernt immer noch keine Zusicherungen aus der binären Dex-Datei.
Zeiger Null
Ich habe das gleiche Problem @PointerNull. behaupten, nicht entfernt.
Mahdi
3

Sie können Zusicherungen verwenden, aber es erfordert einige Arbeit, um sie zuverlässig zu verwenden. Die Systemeigenschaft debug.assertist unzuverlässig. siehe Ausgaben 175697 , 65183 , 36786 und 17324 .

Eine Methode besteht darin, jede assertAnweisung in etwas zu übersetzen , mit dem sich jede Laufzeit befassen kann. Tun Sie dies mit einem Quell-Präprozessor vor dem Java-Compiler. Nehmen Sie zum Beispiel diese Aussage:

assert x == 0: "Failure message";

Bei einem Debug-Build würde Ihr Präprozessor das Obige in eine ifAnweisung übersetzen:

{ if( !(x == 0) ) throw new AssertionError( "Failure message" ); }

Für einen Produktionsaufbau zu einer leeren Anweisung:

;

Beachten Sie, dass dies die Behauptungen zur Erstellungszeit im Gegensatz zur Laufzeit (die übliche Praxis) steuern würde.

Ich konnte keinen vorgefertigten Präprozessor finden, also habe ich einen geschrieben . Siehe den Teil, der sich mit Behauptungen befasst. Die Lizenz zum Kopieren finden Sie hier .

Michael Allan
quelle
1

Zulaxias Antwort zum Entfernen von Junit - Proguard ist bereits Teil von Android SDK / Eclipse. Auf der folgenden Seite erfahren Sie, wie Sie es aktivieren.

http://developer.android.com/guide/developing/tools/proguard.html

Außerdem funktioniert das oben Gesagte nicht mit der neuesten Standard-Proguard-Konfiguration, da das Flag -dontoptimize verwendet wird, das entfernt und einige der Optimierungen aktiviert werden müssen.

Sean Moore
quelle
0

Verwenden Sie das Standard-Java- Assert- Schlüsselwort, zum Beispiel:

assert a==b;

Damit dies funktioniert, müssen Sie eine Zeile zu /system/build.prop hinzufügen und das Telefon neu starten:

debug.assert=1

Dies würde auf gerooteten Telefonen funktionieren. Verwenden Sie einen Dateimanager, der build.prop bearbeiten kann (z. B. X-plore).

Pluspunkte: Die meisten (alle?) Android-Telefone werden mit deaktivierten Zusicherungen ausgeliefert. Selbst wenn Ihr Code versehentlich auf false gesetzt wird, wird die App nicht unterbrochen oder stürzt ab. Auf Ihrem Entwicklungsgerät wird jedoch eine Assertionsausnahme angezeigt.

Zeiger Null
quelle