AndroidアプリでAssertキーワードを使用して、一部のケースでエミュレーターまたはテスト中にデバイスを破壊したいのですが。これは可能ですか?
エミュレータは私の主張を無視しているようです。
回答:
APIはJUnitアサートを提供します。
できるよ
import static junit.framework.Assert.*;
これで、junitフレームワークで提供されるassertTrue、assertEquals、assertNullなどのすべての関数を使用できます。
Junit4フレームワークをEclipseからインポートしないように注意してください。これはorg.junitパッケージになります。Androidデバイスまたはエミュレーターで動作させるには、junit.frameworkパッケージを使用する必要があります。
Embedded VM Controlドキュメント(ソースツリーの生のHTML 、または適切にフォーマットされたコピー)を参照してください。
基本的に、.dexバイトコードにチェックを実行するコードが含まれていても、Dalvik VMはデフォルトでアサーションチェックを無視するように設定されています。アサーションのチェックは、次の2つの方法のいずれかでオンになります。
(1)システムプロパティ「debug.assert」を次のように設定します。
adb shell setprop debug.assert 1
これを行った後にアプリを再インストールする限り、私はこれが意図したとおりに機能することを確認しました、または
(2)コマンドライン引数「--enable-assert」をdalvik VMに送信することにより、アプリ開発者が行うことができない可能性がある可能性があります(ここで間違っている場合は誰かが私を修正します)。
基本的に、グローバル、パッケージレベル、またはクラスレベルで設定できるフラグがあり、それぞれのレベルでアサーションを有効にします。フラグはデフォルトでオフになっているため、アサーションチェックはスキップされます。
サンプルアクティビティに次のコードを記述しました。
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;
}
}
このコードの場合、生成されるdalvikバイトコードは(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
静的コンストラクターがClassオブジェクトのdesiredAssertionStatusメソッドを呼び出し、クラス全体の変数$ assertionsDisabledを設定する方法に注意してください。また、onCreate()では、java.lang.AssertionErrorをスローするすべてのコードがコンパイルされていますが、その実行は、静的コンストラクターのClassオブジェクトに設定されている$ assertionsDisabledの値に依存しています。
JUnitのAssertクラスが主に使用されているようですので、それを使用するのが安全策と思われます。assertキーワードの柔軟性は、開発時にアサーションをオンにし、出荷段階でオフにして、代わりに正常に失敗する機能です。
お役に立てれば。
import static junit.framework.Assert.*
がそのメソッドの1つを使用している場合assertNotNull("It's null!", someObject);
、このアサーションは出荷ビットでオフになっていますか?
adb shell setprop debug.assert 1
Eclipseで行う方法はありますか?
su
、次にsetprop debug.assert 1
。逆アセンブルしたコードは、リリースビルド(stackoverflow.com/a/5590378/506073)に残ります。javacコンパイラーがアサーションを発行しないように指示できるとは思わないので、なんらかの方法で除去する必要があります。そのための簡単な解決策は、キーワードassertを独自の関数にラップして、proguardが削除できるようにすることです。
アサーションが有効になってassert
いるAssertionError
場合、ブール式がの場合、キーワードは単にをスローしfalse
ます。
したがって、IMO、最良の代替、特に。junitに依存したくない場合は、AssertionError
以下に示すように明示的にスローします。
assert x == 0 : "x = " + x;
上記のステートメントの代替は次のとおりです。
Utils._assert(x == 0, "x = " + x);
メソッドは次のように定義されています。
public static void _assert(boolean condition, String message) {
if (!condition) {
throw new AssertionError(message);
}
}
OracleのJavaドキュメントではAssertionError
、許容可能な代替としてをスローすることを推奨しています。
量産コードのこれらの呼び出しを取り除くようにProguardを構成できると思います。
「Android in Practice」では、以下を使用することをお勧めします。
$adb shell setprop dalvik.vm.enableassertions all
この設定が電話で永続化されていない場合は、次のようなプロパティを使用して/data/local.propファイルを作成できます。
dalvik.vm.enableassertions=all
chmod 644
)。
グーグルで問題をチェックアウトするまで、アサーションが機能しなかったのは私を驚かせていました...単純なアサーションをあきらめ、junitsアサーションメソッドを使用します。
便宜上、私は以下を使用しています。
import static junit.framework.Assert。*;
静的インポートのため、後で書くことができます:
assertTrue(...); Assert.assertTrue(...);の代わりに
JUnitアサート(または他のクラスパス)を含むコードの配布に懸念がある場合は、ProGuard構成オプション 'assumenosideeffects'を使用できます。 。
例えば。
-assumenosideeffects junit.framework.Assert {
*;
}
すべてのテストメソッドを入れた共通のデバッグライブラリがあり、このオプションを使用して、リリースされたアプリからそれを取り除きます。
これにより、リリースコードでは使用されない操作されている文字列の特定が困難な問題も解消されます。たとえば、デバッグログメソッドを記述し、そのメソッドで文字列をログに記録する前にデバッグモードを確認する場合、文字列を作成し、メモリを割り当て、メソッドを呼び出しますが、何もしないことを選択します。クラスを取り除くと、呼び出しが完全に削除されます。つまり、文字列がメソッド呼び出し内で作成されている限り、文字列も削除されます。
ただし、行を取り除くだけで本当に安全であることを確認してください。これは、ProGuardの部分をチェックせずに行われるためです。voidを返すメソッドを削除しても問題ありませんが、削除するものから戻り値を取得する場合は、実際の操作ロジックでそれらを使用していないことを確認してください。
-assumenosideeffects class junit.framework.Assert { *; }
アサーションを使用できますが、アサーションを確実に使用するにはいくつかの作業が必要です。システムプロパティdebug.assert
は信頼できません。問題参照175697、65183、36786および17324を。
1つの方法は、各assert
ステートメントを任意のランタイムが処理できるものに変換することです。これは、Javaコンパイラの前にあるソースプリプロセッサで行います。たとえば、次のステートメントを見てください。
assert x == 0: "Failure message";
デバッグビルドの場合、プリプロセッサは上記をif
ステートメントに変換します。
{ if( !(x == 0) ) throw new AssertionError( "Failure message" ); }
本番ビルドの場合、空のステートメントに:
;
これは、実行時(通常の方法)ではなく、ビルド時にアサーションを制御することに注意してください。
既成のプリプロセッサが見つからなかったため、スクリプトを作成しました。アサーションを扱う部分を参照してください。コピーするライセンスはこちらです。
Junitの除去に関するZulaxiaの回答に追加するには-ProguardはすでにAndroid SDK / Eclipseの一部であり、次のページでそれを有効にする方法を説明しています。
http://developer.android.com/guide/developing/tools/proguard.html
また、上記の例では、-dontoptimizeフラグを使用する必要があるため、最新のデフォルトのプロガード構成では機能せず、一部の最適化がオンになっています。
たとえば、標準のJava アサートキーワードを使用します。
assert a==b;
これを機能させるには、/ system / build.propに1行追加して、電話を再起動する必要があります。
debug.assert=1
これは根ざした電話で動作します。build.prop(X-ploreなど)を編集できるファイルマネージャーを使用します。
プラス:ほとんど(すべて?)のAndroidスマートフォンはアサーションを無効にして出荷されます。コードが誤ってfalseにアサートされた場合でも、アプリは中断またはクラッシュしません。ただし、開発デバイスではアサーション例外が発生します。