Posso usar o assert em dispositivos Android?

88

Quero usar a palavra-chave Assert em meus aplicativos Android para destruir meu aplicativo em alguns casos no emulador ou meu dispositivo durante o teste. Isso é possível?

Parece que o emulador simplesmente ignora minhas afirmações.

Janusz
fonte
2
Para ART, em vez de Dalvik, consulte stackoverflow.com/questions/35997703/…
fadden
Observe que a resposta aceita é bastante enganosa.
reduzindo a atividade de

Respostas:

-7

A API fornece o JUnit Assert .

Você pode fazer

import static junit.framework.Assert.*;

agora você pode usar todas as funções como assertTrue, assertEquals, assertNull que são fornecidas na estrutura junit.

Tenha cuidado para não importar a estrutura Junit4 por meio do eclipse, que seria o pacote org.junit. Você precisa usar o pacote junit.framework para fazê-lo funcionar em um dispositivo Android ou emulador.

JRL
fonte
51
Bem, o OP pediu “palavra-chave assert” que - ao contrário de junit.framework.Assert pode ser otimizada pelo JIT. E por isso mesmo vim aqui. Espero que algumas das outras respostas sejam mais úteis.
Martin
27
Eu odeio ser rude, mas essa não deve ser a resposta aceita porque não responde à pergunta (concordo com o comentário de @Martin). Outras respostas explicam como fazer a palavra-chave assert funcionar corretamente, por exemplo, execute "adb shell setprop debug.assert 1"
jfritz42
3
Votos negados porque OP perguntou sobre a palavra-chave assert. @scorpiodawg descreveu o processo abaixo: stackoverflow.com/a/5563637/484261
A resposta de scorpiodawg veio apenas um ano depois, então acho que essa resposta foi aceita apenas porque o OP, por algum motivo, se sentiu obrigado a marcar uma resposta como aceita. Essa atitude torna um grande número de respostas no SO incompletas ou completamente terríveis.
assíncrono de
145

Consulte o documento Embedded VM Control (HTML bruto da árvore de origem ou uma cópia bem formatada ).

Basicamente, a VM Dalvik é configurada para ignorar as verificações de asserção por padrão, embora o código de byte .dex inclua o código para realizar a verificação. A verificação de asserções é ativada de duas maneiras:

(1) definindo a propriedade do sistema "debug.assert" via:

adb shell setprop debug.assert 1

que verifiquei funcionar conforme o esperado, contanto que você reinstale seu aplicativo depois de fazer isso, ou

(2) enviando o argumento de linha de comando "--enable-assert" para a VM dalvik, que pode não ser algo que os desenvolvedores de aplicativos sejam capazes de fazer (alguém me corrija se eu estiver errado aqui).

Basicamente, há um sinalizador que pode ser definido globalmente, em um nível de pacote ou em um nível de classe que permite asserções naquele respectivo nível. O sinalizador está desativado por padrão, como resultado do qual as verificações de asserção são ignoradas.

Escrevi o seguinte código em minha atividade de amostra:


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

Para este código, o código de bytes dalvik gerado é (para 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

Observe como o construtor estático invoca o método neededAssertionStatus no objeto Class e define a variável de classe $ assertionsDisabled; também observe que em onCreate (), todo o código para lançar java.lang.AssertionError é compilado, mas sua execução depende do valor de $ assertionsDisabled que é definido para o objeto Class no construtor estático.

Parece que a classe Assert do JUnit é a que é usada predominantemente, então é provável que seja uma aposta segura usá-la. A flexibilidade da palavra-chave assert é a capacidade de ativar assertions no tempo de desenvolvimento e desativá-los para enviar bits e, em vez disso, falhar normalmente.

Espero que isto ajude.

scorpiodawg
fonte
Parece que o Google removeu a fonte navegável (vi referências a isso em respostas a outras perguntas aqui). Sua melhor aposta é obter a fonte ou tentar pesquisá-la em um mecanismo de busca. Procure por "dalvik / embedded-vm-control.html". Aqui está um lugar que o contém: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/… . Espero que isto ajude.
scorpiodawg
Muito útil, obrigado. Estou confuso com a distinção feita no penúltimo parágrafo, no entanto. Estamos discutindo dois tipos de assertivas: o primeiro sendo os métodos do pacote JUnit Assert como assertNotNull (), e o segundo sendo a palavra-chave 'assert' da linguagem Java. Sua resposta se aplica a ambos? Por exemplo, se eu import static junit.framework.Assert.*e depois usar um de seus métodos, como assertNotNull("It's null!", someObject);, essa afirmação está desativada em bits de envio?
Jeffro
1
Olá, Jeffro, não acredito - junit.framework.Assert é simplesmente uma classe que lança uma exceção quando a condição de entrada é considerada falsa. A palavra-chave assert, por outro lado, está incorporada ao idioma. Espero que isto ajude.
scorpiodawg
3
Existe uma maneira de fazer adb shell setprop debug.assert 1no Eclipse?
Pacerier
1
Asserções também podem ser feitas a partir de um terminal em execução no dispositivo se você for root. suEntão, primeiro setprop debug.assert 1. Observe que o código que você mostra desmontado permanecerá em uma versão de compilação ( stackoverflow.com/a/5590378/506073 ). Não acredito que o compilador javac possa ser instruído a não emitir asserções, portanto, elas precisam ser eliminadas de alguma forma. Uma solução simples para isso é envolver a palavra-chave assert em sua própria função que o proguard pode remover para você.
ahcox
10

Quando as asserções estão habilitadas, a assertpalavra - chave simplesmente lança um AssertionErrorquando a expressão booleana é false.

Então, IMO, a melhor alternativa, esp. se você é avesso a depender de junit, é lançar um AssertionErrorexplicitamente como mostrado abaixo:

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

Uma alternativa para a declaração acima é:

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

Onde o método é definido como:

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

Os documentos do Oracle Java recomendam lançar um AssertionErrorcomo alternativa aceitável.

Eu acho que você pode configurar o Proguard para eliminar essas chamadas de código de produção.

Dheeraj Vepakomma
fonte
Mas como você habilita asserções? No Android Studio?
SMBiggs
8

Em "Android na prática", sugere-se o uso de:

$adb shell setprop dalvik.vm.enableassertions all

se essas configurações não persistirem em seu telefone, você pode criar o arquivo /data/local.prop com propriedades como:

dalvik.vm.enableassertions=all
Marcinj
fonte
Por stackoverflow.com/a/18556839/2004714 , você também pode precisar certificar-se de que o arquivo seja somente leitura ( chmod 644).
Paulo
5

Estava me incomodando muito, que minhas afirmações não funcionassem, até que verifiquei o problema no google ... Desisti das afirmações simples e irei com os métodos de asserção junits.

Para fins de conveniência, estou usando:

import static junit.framework.Assert. *;

Devido à importação estática, posso escrever mais tarde:

assertTrue (...); em vez de Assert.assertTrue (...);

Ready4Android
fonte
4

Se você estiver preocupado com o envio de código com o JUnit asserts in (ou qualquer outro caminho de classe), você pode usar a opção de configuração do ProGuard 'assumenosideeffects', que removerá um caminho de classe na suposição de que removê-lo não afeta o código .

Por exemplo.

-assumenosideeffects junit.framework.Assert {
*;
}

Eu tenho uma biblioteca de depuração comum onde coloquei todos os meus métodos de teste e, em seguida, uso esta opção para retirá-la dos meus aplicativos lançados.

Isso também elimina o problema de difícil localização de cadeias de caracteres manipuladas que nunca são usadas no código de lançamento. Por exemplo, se você escrever um método de log de depuração e, nesse método, verificar o modo de depuração antes de registrar a string, ainda estará construindo a string, alocando memória, chamando o método, mas optando por não fazer nada. Remover a classe remove as chamadas inteiramente, o que significa que, desde que sua string seja construída dentro da chamada do método, ela também desaparecerá.

No entanto, certifique-se de que é genuinamente seguro apenas remover as linhas, pois isso é feito sem verificação por parte do ProGuard. A remoção de qualquer método de retorno nulo será suficiente, no entanto, se você estiver obtendo quaisquer valores de retorno de tudo o que está removendo, certifique-se de não usá-los para a lógica operacional real.

Zulaxia
fonte
1
Acho que a sintaxe adequada seria:-assumenosideeffects class junit.framework.Assert { *; }
Pooks
Resposta confusa. O comando mencionado causa um erro de proguard. O comando corrigido pelo Pooks ainda não remove as asserções do arquivo dex binário.
Pointer Null
Eu tenho o mesmo problema @PointerNull. afirmar não será removido.
Mahdi
3

Você pode usar asserções, mas dá algum trabalho usá-las de maneira confiável. A propriedade do sistema debug.assertnão é confiável; veja os problemas 175697 , 65183 , 36786 e 17324 .

Um método é traduzir cada assertinstrução em algo com que qualquer tempo de execução possa lidar. Faça isso com um pré-processador de origem na frente do compilador Java. Por exemplo, veja esta declaração:

assert x == 0: "Failure message";

Para uma compilação de depuração, seu pré-processador traduziria o acima em uma ifinstrução:

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

Para uma construção de produção, para uma instrução vazia:

;

Observe que isso controlaria as asserções no tempo de construção, ao contrário do tempo de execução (a prática usual).

Não consegui encontrar nenhum pré-processador pronto, então criei um . Veja a parte que trata das afirmações. Licença para copiar é aqui .

Michael Allan
fonte
1

Para adicionar à resposta da Zulaxia sobre eliminar o Junit - Proguard já faz parte do Android SDK / Eclipse e a página a seguir mostra como habilitá-lo.

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

Além disso, o acima não funcionará com a configuração do programa padrão mais recente porque usa o sinalizador -dontoptimize que deve ser retirado e algumas das otimizações ativadas.

Sean Moore
fonte
0

Use a palavra-chave assert Java padrão , por exemplo:

assert a==b;

Para que isso funcione, você deve adicionar uma linha a /system/build.prop e reiniciar o telefone:

debug.assert=1

Isso funcionaria no telefone enraizado. Use algum gerenciador de arquivos capaz de editar build.prop (por exemplo, X-plore).

Vantagens: a maioria (todos?) Dos telefones Android são fornecidos com as declarações desativadas. Mesmo que seu código seja acidentalmente declarado falso, o aplicativo não interrompe ou falha. No entanto, em seu dispositivo de desenvolvimento, você obterá exceção de asserção.

Pointer Null
fonte