Puis-je utiliser Assert sur les appareils Android?

88

Je souhaite utiliser le mot clé Assert dans mes applications Android pour détruire mon application dans certains cas sur l'émulateur ou sur mon appareil pendant le test. Est-ce possible?

Il semble que l'émulateur ignore simplement mes affirmations.

Janusz
la source
2
Pour ART, plutôt que Dalvik, voir stackoverflow.com/questions/35997703/…
fadden
Notez que la réponse acceptée est assez trompeuse.
activité réduite

Réponses:

-7

L'API fournit l' assert JUnit .

Tu peux faire

import static junit.framework.Assert.*;

maintenant vous pouvez utiliser toutes les fonctions telles que assertTrue, assertEquals, assertNull qui sont fournies dans le framework junit.

Faites attention de ne pas importer le framework Junit4 via eclipse, ce serait le package org.junit. Vous devez utiliser le package junit.framework pour le faire fonctionner sur un appareil Android ou l'émulateur.

JRL
la source
51
Eh bien, l'OP a demandé «assert keyword» qui - contrairement à junit.framework.Assert peut être optimisé par le JIT. Et c'est précisément pour cette raison que je suis venu ici. J'espère que certaines des autres réponses seront plus utiles.
Martin
27
Je déteste être impoli mais cela ne devrait pas être la réponse acceptée car elle ne répond pas à la question (je suis d'accord avec le commentaire de @Martin). D'autres réponses expliquent comment faire fonctionner correctement le mot-clé assert, par exemple exécutez "adb shell setprop debug.assert 1"
jfritz42
3
Évalué car OP a posé une question sur le mot clé assert @scorpiodawg a décrit le processus ci-dessous: stackoverflow.com/a/5563637/484261
La réponse de scorpiodawg est venue seulement un an plus tard, donc je suppose que cette réponse n'a été acceptée que parce que le PO, pour une raison quelconque, s'est senti obligé de marquer une réponse comme acceptée. Cette attitude rend un grand nombre de réponses sur SO incomplètes ou carrément horribles.
async
145

Voir le document Embedded VM Control (HTML brut de l' arborescence source ou une copie bien formatée ).

Fondamentalement, la VM Dalvik est configurée pour ignorer les vérifications d'assertion par défaut, même si le code d'octet .dex inclut le code pour effectuer la vérification. La vérification des assertions est activée de l'une des deux manières suivantes:

(1) en définissant la propriété système "debug.assert" via:

adb shell setprop debug.assert 1

que j'ai vérifié fonctionne comme prévu tant que vous réinstallez votre application après cela, ou

(2) en envoyant l'argument de ligne de commande "--enable-assert" à la VM dalvik, ce qui pourrait ne pas être quelque chose que les développeurs d'applications sont susceptibles de faire (quelqu'un me corrige si je me trompe ici).

Fondamentalement, il existe un indicateur qui peut être défini soit globalement, au niveau du package, soit au niveau d'une classe qui permet des assertions à ce niveau respectif. L'indicateur est désactivé par défaut, ce qui fait que les vérifications d'assertion sont ignorées.

J'ai écrit le code suivant dans mon exemple d'activité:


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

Pour ce code, le code d'octet dalvik généré est (pour 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

Remarquez comment le constructeur statique appelle la méthode souhaitéeAssertionStatus sur l'objet Class et définit la variable à l'échelle de la classe $ assertionsDisabled; notez également que dans onCreate (), tout le code dans lequel lancer java.lang.AssertionError est compilé, mais son exécution dépend de la valeur de $ assertionsDisabled qui est définie pour l'objet Class dans le constructeur statique.

Il semble que la classe Assert de JUnit soit ce qui est principalement utilisé, il est donc probablement une valeur sûre de l'utiliser. La flexibilité du mot-clé assert est la possibilité d'activer les assertions au moment du développement et de les désactiver pour l'envoi de bits et d'échouer à la place correctement.

J'espère que cela t'aides.

scorpiodawg
la source
Il semble que Google ait supprimé la source consultable (j'ai vu des références à cela dans les réponses à d'autres questions ici). Votre meilleur pari est soit d'obtenir la source, soit d'essayer de la rechercher dans un moteur de recherche. Recherchez "dalvik / embedded-vm-control.html". Voici un endroit qui l'a: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/… . J'espère que cela t'aides.
scorpiodawg
Très utile, merci. Je suis cependant déconcerté par la distinction faite dans l'avant-dernier paragraphe. Nous discutons de deux types d'assertions: la première étant les méthodes du package JUnit Assert comme assertNotNull (), et la seconde étant le mot-clé «assert» du langage Java. Votre réponse s'applique-t-elle aux deux? Par exemple, si j'utilise import static junit.framework.Assert.*, puis j'utilise l'une de ses méthodes, par exemple assertNotNull("It's null!", someObject);, cette assertion est-elle désactivée dans les bits d'expédition?
Jeffro
1
Salut Jeffro, je ne le crois pas - junit.framework.Assert est simplement une classe qui lève une exception lorsque la condition d'entrée s'avère fausse. Le mot-clé assert, quant à lui, est intégré au langage. J'espère que cela t'aides.
scorpiodawg
3
Y a-t-il un moyen de faire adb shell setprop debug.assert 1dans Eclipse?
Pacerier
1
Les assertions peuvent également être effectuées à partir d'un terminal fonctionnant sur l'appareil si vous êtes root. D'abord su, alors setprop debug.assert 1. Notez que le code que vous montrez désassemblé restera dans une version de version ( stackoverflow.com/a/5590378/506073 ). Je ne crois pas qu'on puisse dire au compilateur javac de ne pas émettre d'assertions, donc elles doivent être supprimées d'une manière ou d'une autre. Une solution simple à cela est d'envelopper le mot-clé assert dans votre propre fonction que proguard peut supprimer pour vous.
ahcox
10

Lorsque les assertions sont activées, le assertmot clé lève simplement un AssertionErrorlorsque l'expression booléenne l'est false.

Donc IMO, la meilleure alternative, esp. si vous ne voulez pas dépendre de junit, lancez un AssertionErrorexplicitement comme indiqué ci-dessous:

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

Une alternative à la déclaration ci-dessus est:

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

Où la méthode est définie comme:

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

La documentation Oracle Java recommande de lancer un AssertionErrorcomme alternative acceptable.

Je suppose que vous pouvez configurer Proguard pour supprimer ces appels au code de production.

Dheeraj Vepakomma
la source
Mais comment activer les assertions? Sur Android Studio?
SMBiggs
8

Dans "Android en pratique", il est suggéré d'utiliser:

$adb shell setprop dalvik.vm.enableassertions all

si ces paramètres ne sont pas conservés sur votre téléphone, vous pouvez créer un fichier /data/local.prop avec des propriétés telles que:

dalvik.vm.enableassertions=all
Marcinj
la source
Par stackoverflow.com/a/18556839/2004714 , vous devrez peut-être également vous assurer que le fichier est en lecture seule ( chmod 644).
Paulo
5

Cela me dérangeait, que mes affirmations ne fonctionnaient pas, jusqu'à ce que je vérifie le problème sur Google ... J'ai abandonné les affirmations simples et j'irai avec les méthodes d'assertion junits.

À des fins de commodité, j'utilise:

import statique junit.framework.Assert. *;

En raison de l'importation statique, je peux écrire plus tard:

assertTrue (...); au lieu de Assert.assertTrue (...);

Ready4Android
la source
4

Si vous êtes préoccupé par le code d'expédition avec les assertions JUnit dans (ou tout autre chemin de classe), vous pouvez utiliser l'option de configuration ProGuard `` assumenosideeffects '', qui supprimera un chemin de classe en supposant que sa suppression ne fait rien au code .

Par exemple.

-assumenosideeffects junit.framework.Assert {
*;
}

J'ai une bibliothèque de débogage commune dans laquelle j'ai mis toutes mes méthodes de test, puis j'utilise cette option pour la supprimer de mes applications publiées.

Cela supprime également le problème difficile à repérer des chaînes manipulées qui ne sont jamais utilisées dans le code de version. Par exemple, si vous écrivez une méthode de journal de débogage, et dans cette méthode, vous vérifiez le mode débogage avant de consigner la chaîne, vous construisez toujours la chaîne, allouez de la mémoire, appelez la méthode, mais choisissez de ne rien faire. Supprimer la classe supprime alors entièrement les appels, ce qui signifie que tant que votre chaîne est construite à l'intérieur de l'appel de méthode, elle disparaît également.

Assurez-vous qu'il est vraiment sûr de simplement retirer les lignes, car cela se fait sans vérification de la part de ProGuard. La suppression de toute méthode de retour vide conviendra, mais si vous prenez des valeurs de retour de ce que vous supprimez, assurez-vous de ne pas les utiliser pour la logique opérationnelle réelle.

Zulaxia
la source
1
Je pense que la syntaxe appropriée serait:-assumenosideeffects class junit.framework.Assert { *; }
Pooks
Réponse déroutante. La commande mentionnée provoque une erreur de proguard. La commande corrigée par Pooks ne supprime toujours pas les assertions du fichier dex binaire.
Pointer Null
J'ai le même problème @PointerNull. assert ne sera pas supprimé.
Mahdi
3

Vous pouvez utiliser des assertions, mais il faut du travail pour les utiliser de manière fiable. Propriété systèmedebug.assert n'est pas fiable; voir les numéros 175697 , 65183 , 36786 et 17324 .

Une méthode consiste à traduire chaque assertinstruction en quelque chose que n'importe quel environnement d'exécution peut gérer. Faites cela avec un préprocesseur source devant le compilateur Java. Par exemple, prenez cette déclaration:

assert x == 0: "Failure message";

Pour une version de débogage, votre préprocesseur traduirait ce qui précède en une ifinstruction:

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

Pour une version de production, vers une instruction vide:

;

Notez que cela contrôlerait les assertions au moment de la construction, par opposition à l'exécution (la pratique habituelle).

Je n'ai pas pu trouver de préprocesseur prêt à l'emploi, alors j'en ai écrit un . Voir la partie traitant des assertions. La licence de copie est ici .

Michael Allan
la source
1

Pour ajouter à la réponse de Zulaxia sur le retrait de Junit - Proguard fait déjà partie d'Android SDK / Eclipse et la page suivante vous explique comment l'activer.

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

De plus, ce qui précède ne fonctionnera pas avec la dernière configuration de proguard par défaut car il utilise l'indicateur -dontoptimize qui doit être retiré et certaines des optimisations activées.

Sean Moore
la source
0

Utilisez le mot clé assert Java standard , par exemple:

assert a==b;

Pour que cela fonctionne, vous devez ajouter une ligne à /system/build.prop et redémarrer le téléphone:

debug.assert=1

Cela fonctionnerait sur un téléphone enraciné. Utilisez un gestionnaire de fichiers capable d'éditer build.prop (par exemple X-plore).

Avantages: la plupart (tous?) Des téléphones Android sont livrés avec les assertions désactivées. Même si votre code affirme accidentellement la valeur false, l'application ne s'interrompra ni ne plantera. Cependant, sur votre appareil de développement, vous obtiendrez une exception d'assertion.

Pointeur nul
la source