Czy mogę używać assert na urządzeniach z Androidem?

88

Chcę użyć słowa kluczowego Assert w moich aplikacjach na Androida, aby w niektórych przypadkach zniszczyć moją aplikację na emulatorze lub na moim urządzeniu podczas testowania. czy to możliwe?

Wygląda na to, że emulator po prostu ignoruje moje zapewnienia.

Janusz
źródło
2
W przypadku ART, a nie Dalvik, zobacz stackoverflow.com/questions/35997703/…
fadden
1
Zauważ, że przyjęta odpowiedź jest dość myląca.
aktywność zmniejszająca

Odpowiedzi:

-7

Interfejs API zapewnia JUnit Assert .

Możesz to zrobić

import static junit.framework.Assert.*;

teraz możesz używać wszystkich funkcji, takich jak assertTrue, assertEquals, assertNull, które są udostępniane w ramach junit.

Uważaj, aby nie importować frameworka Junit4 przez eclipse, to byłby pakiet org.junit. Musisz użyć pakietu junit.framework, aby działał na urządzeniu z systemem Android lub w emulatorze.

JRL
źródło
51
Cóż, OP poprosił o „assert keyword”, który - w przeciwieństwie do junit.framework.Assert - może zostać zoptymalizowany przez JIT. I właśnie z tego powodu tu przyjechałem. Mam nadzieję, że niektóre inne odpowiedzi będą bardziej pomocne.
Martin
27
Nienawidzę być niegrzeczny, ale nie powinna to być akceptowana odpowiedź, ponieważ nie odpowiada na pytanie (zgadzam się z komentarzem @Martin). Inne odpowiedzi wyjaśniają, jak sprawić, by słowo kluczowe assert działało poprawnie, np. Uruchom "adb shell setprop debug.assert 1"
jfritz42
3
Ocena negatywna, ponieważ OP zapytał o słowo kluczowe assert. @scorpiodawg przedstawia proces poniżej: stackoverflow.com/a/5563637/484261
Odpowiedź scorpiodawga nadeszła zaledwie rok później, więc myślę, że ta odpowiedź została zaakceptowana tylko dlatego, że OP z jakiegoś powodu czuł się zobowiązany do oznaczenia odpowiedzi jako zaakceptowanej. Taka postawa sprawia, że ​​ogromna liczba odpowiedzi na SO jest niepełna lub wręcz okropna.
async
145

Zobacz dokument Embedded VM Control (surowy kod HTML z drzewa źródłowego lub ładnie sformatowana kopia).

Zasadniczo maszyna wirtualna Dalvik jest ustawiona tak, aby domyślnie ignorować sprawdzanie potwierdzenia, nawet jeśli kod bajtowy .dex zawiera kod do wykonania sprawdzenia. Sprawdzanie potwierdzeń jest włączane na jeden z dwóch sposobów:

(1) ustawiając właściwość systemową „debug.assert” przez:

adb shell setprop debug.assert 1

co sprawdziłem, działa zgodnie z przeznaczeniem, o ile po wykonaniu tej czynności ponownie zainstalujesz aplikację lub

(2) wysyłając argument wiersza poleceń „--enable-assert” do maszyny wirtualnej dalvik, co może nie być czymś, co twórcy aplikacji prawdopodobnie nie będą w stanie zrobić (ktoś poprawi mnie, jeśli się mylę).

Zasadniczo istnieje flaga, którą można ustawić globalnie, na poziomie pakietu lub na poziomie klasy, co umożliwia asercje na tym odpowiednim poziomie. Flaga jest domyślnie wyłączona, w wyniku czego kontrole potwierdzeń są pomijane.

Napisałem następujący kod w mojej przykładowej aktywności:


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

Dla tego kodu generowany kod bajtowy dalvik (dla systemu 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

Zwróć uwagę, jak konstruktor statyczny wywołuje metodę pożądanąAssertionStatus w obiekcie Class i ustawia zmienną dla całej klasy $ assertionsDisabled; Zauważ również, że w onCreate () cały kod do zgłaszania java.lang.AssertionError jest wkompilowany, ale jego wykonanie jest zależne od wartości $ assertionsDisabled, która jest ustawiona dla obiektu Class w konstruktorze statycznym.

Wygląda na to, że klasa Assert JUnit jest tym, co jest używane przeważnie, więc prawdopodobnie jest to bezpieczne. Elastyczność słowa kluczowego assert polega na możliwości włączania asercji w czasie programowania i wyłączania ich w celu wysyłania bitów, a zamiast tego bezproblemowo zawodzą.

Mam nadzieję że to pomoże.

scorpiodawg
źródło
Wygląda na to, że Google usunęło źródło, które można przeglądać (widziałem odniesienia do tego w odpowiedziach na inne pytania tutaj). Najlepszym rozwiązaniem jest zdobycie źródła lub próba wyszukania go w wyszukiwarce. Wyszukaj „dalvik / embedded-vm-control.html”. Oto jedno miejsce, w którym to ma: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/… . Mam nadzieję że to pomoże.
scorpiodawg
Bardzo przydatne, dziękuję. Jestem jednak zdezorientowany rozróżnieniem dokonanym w przedostatnim akapicie. Omawiamy dwa rodzaje potwierdzeń: pierwszy to metody pakietu JUnit Assert, takie jak assertNotNull (), a drugi to słowo kluczowe „assert” języka Java. Czy Twoja odpowiedź dotyczy obu? Na przykład, jeśli ja import static junit.framework.Assert.*i następnie użyję jednej z jego metod, na przykład assertNotNull("It's null!", someObject);, czy to potwierdzenie jest wyłączone w bitach wysyłkowych?
Jeffro
1
Cześć Jeffro, nie sądzę - junit.framework.Assert to po prostu klasa, która zgłasza wyjątek, gdy warunek wejściowy jest fałszywy. Z drugiej strony słowo kluczowe assert jest wbudowane w język. Mam nadzieję że to pomoże.
scorpiodawg
3
Czy jest sposób na zrobienie tego adb shell setprop debug.assert 1w Eclipse?
Pacerier,
1
Asercje można również wykonywać z terminala działającego na urządzeniu, jeśli jesteś rootem. Po pierwsze su, następnie setprop debug.assert 1. Zwróć uwagę, że kod, który pokazujesz zdemontowany, pozostanie w kompilacji wydania ( stackoverflow.com/a/5590378/506073 ). Nie wierzę, że kompilatorowi javac można powiedzieć, aby nie emitował asercji, więc należy je jakoś usunąć. Prostym rozwiązaniem jest umieszczenie słowa kluczowego assert we własnej funkcji, którą program Proguard może dla Ciebie usunąć.
ahcox
10

Gdy asercje są włączone, assertsłowo kluczowe po prostu zgłasza, AssertionErrorgdy wyrażenie boolowskie jest false.

Zatem IMO, najlepsza alternatywa, zwł. jeśli nie chcesz polegać na junicie, rzuć AssertionErrorjawnie, jak pokazano poniżej:

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

Alternatywą dla powyższego stwierdzenia jest:

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

Gdzie metoda jest zdefiniowana jako:

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

Dokumentacja java Oracle zaleca zgłoszenie AssertionErrorjako akceptowalnej alternatywy.

Wydaje mi się, że możesz skonfigurować Proguard, aby wyeliminować te wywołania kodu produkcyjnego.

Dheeraj Vepakomma
źródło
Ale jak włączyć asercje? Korzystasz z Android Studio?
SMBiggs
8

W „Android w praktyce” sugeruje się użycie:

$adb shell setprop dalvik.vm.enableassertions all

jeśli te ustawienia nie są utrwalone w telefonie, możesz utworzyć plik /data/local.prop z właściwościami takimi jak:

dalvik.vm.enableassertions=all
marcinj
źródło
W przypadku stackoverflow.com/a/18556839/2004714 może być również konieczne upewnienie się, że plik jest przeznaczony tylko do odczytu ( chmod 644).
Paulo
5

Diabelnie mnie to denerwowało, że moje twierdzenia nie zadziałały, dopóki nie sprawdziłem problemu w google ... Zrezygnowałem z prostych twierdzeń i pójdę z metodami asercji junits.

Dla wygody używam:

import static junit.framework.Assert. *;

Ze względu na import statyczny mogę później napisać:

assertTrue (...); zamiast Assert.assertTrue (...);

Ready4Android
źródło
4

Jeśli obawiasz się wysyłania kodu z potwierdzeniami JUnit w (lub dowolnej innej ścieżce klas), możesz użyć opcji konfiguracyjnej ProGuard „assumenosideeffects”, która usunie ścieżkę klasy przy założeniu, że usunięcie go nic nie zmienia z kodem .

Na przykład.

-assumenosideeffects junit.framework.Assert {
*;
}

Mam wspólną bibliotekę debugowania, w której umieszczam wszystkie moje metody testowania, a następnie używam tej opcji, aby usunąć ją z moich wydanych aplikacji.

Eliminuje to również trudny do zauważenia problem manipulowania napisami, które nigdy nie są używane w kodzie wydania. Na przykład, jeśli napiszesz metodę dziennika debugowania i w tej metodzie sprawdzisz tryb debugowania przed zarejestrowaniem ciągu, nadal konstruujesz ciąg, alokujesz pamięć, wywołujesz metodę, ale potem nie robisz nic. Usunięcie klasy, a następnie całkowicie usuwa wywołania, co oznacza, że ​​tak długo, jak łańcuch jest zbudowany wewnątrz wywołania metody, również znika.

Upewnij się jednak, że po prostu usunięcie linek jest naprawdę bezpieczne, ponieważ odbywa się to bez sprawdzania części ProGuard. Usunięcie metody zwracającej void będzie w porządku, jednak jeśli pobierasz jakiekolwiek wartości zwracane z tego, co usuwasz, upewnij się, że nie używasz ich do rzeczywistej logiki operacyjnej.

Zulaxia
źródło
1
Myślę, że właściwa składnia byłaby następująca:-assumenosideeffects class junit.framework.Assert { *; }
Pooks
Myląca odpowiedź. Wspomniane polecenie powoduje błąd programu. Polecenie poprawione przez Pooks nadal nie usuwa asercji z binarnego pliku dex.
Pointer Null
Mam ten sam problem @PointerNull. assert nie zostanie usunięty.
Mahdi
3

Możesz używać asercji, ale ich niezawodne użycie wymaga trochę pracy. Właściwość systemu debug.assertjest zawodna; patrz numery 175697 , 65183 , 36786 i 17324 .

Jedną z metod jest przetłumaczenie każdej assertinstrukcji na coś, z czym może sobie poradzić każde środowisko wykonawcze. Zrób to z preprocesorem źródła przed kompilatorem Java. Weźmy na przykład to stwierdzenie:

assert x == 0: "Failure message";

W przypadku kompilacji do debugowania preprocesor przetłumaczy powyższe na ifinstrukcję:

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

W przypadku kompilacji produkcyjnej do pustej instrukcji:

;

Zauważ, że kontrolowałoby to asercje w czasie kompilacji, a nie w czasie wykonywania (zwykła praktyka).

Nie mogłem znaleźć gotowego preprocesora, więc napisałem jeden z nich . Zobacz część dotyczącą twierdzeń. Licencja na kopiowanie jest tutaj .

Michael Allan
źródło
1

Aby dodać do odpowiedzi Zulaxii na temat usuwania Junit - Proguard jest już częścią Android SDK / Eclipse, a na następnej stronie opisano, jak go włączyć.

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

Powyższe również nie będzie działać z najnowszą domyślną konfiguracją proguard, ponieważ używa flagi -dontoptimize, którą należy usunąć i włączyć niektóre optymalizacje.

Sean Moore
źródło
0

Użyj standardowego słowa kluczowego assert Java , na przykład:

assert a==b;

Aby to zadziałało, musisz dodać jedną linię do /system/build.prop i zrestartować telefon:

debug.assert=1

To zadziała na zrootowanym telefonie. Użyj jakiegoś menedżera plików, który może edytować build.prop (np. X-plore).

Plusy: większość (wszystkich?) Telefonów z Androidem jest dostarczanych z wyłączonymi asercjami. Nawet jeśli Twój kod przypadkowo potwierdzi fałsz, aplikacja nie przerywa pracy ani nie ulega awarii. Jednak na swoim urządzeniu programistycznym otrzymasz wyjątek asercji.

Pointer Null
źródło