9

I am writing my first Android application and I am liberally using asserts() from junit.framework.Assert

I would like to find a way to ensure that the asserts are only compiled into the debug build, not in the release build.

I know how to query the android:debuggable attribute from the manifest so I could create a variable and accomplish this in the following fashon:

static final boolean mDebug = ...

if (mDebug) Assert.assertNotNull(view);

Is there a better way to do this? i.e. I would prefer not to use an if() with each assert.

thanks

jbww
  • 735
  • 2
  • 12
  • 27

3 Answers3

22

I think the Java language's assert keyword is likely what you want. Under the covers, the assert keyword essentially compiles into Dalvik byte code that does two things:

  1. Checks whether the static variable assertionsDisabled (set in the class' static constructor via a call to java.lang.Class.desiredAssertionStatus()) is != 0 and if so, does nothing
  2. If it is 0, then it checks the assertion expression and throws a java.lang.AssertionError if the expression resolves to false, effectively terminating your application.

The Dalvik runtime by default has assertions turned off, and therefore desiredAssertionStatus always returns 1 (or more precisely, some non-zero value). This is akin to running in "retail" mode. In order to turn on "debug" mode, you can run the following command against the emulator or the device:

adb shell setprop debug.assert 1

and this should do the trick (should work on the emulator or any rooted debugging-ready device).

Note however that the aforementioned Dalvik code that checks the value of assertionsDisabled and throws an AssertionError if the expression is false is always included in your byte code and liberal sprinkling of asserts in your code may lead to byte code bloat.

Please see this for a bit more detail: Can I use assert on Android devices?

Community
  • 1
  • 1
scorpiodawg
  • 5,612
  • 3
  • 42
  • 62
  • This can also be done from a terminal running on the device if you are root. First `su`, then `setprop debug.assert 1`. – ahcox Apr 08 '12 at 11:38
  • Actually, things like `assert !BuildConfig.DEBUG || (expr) : "msg";` can be optimized away by `javac`, which works because the read of `$assertionsDisabled` has no side-effects (not even failing to load the class). A little ugly and unclear, but it gets rid of them in release builds. – tc. Apr 18 '13 at 12:00
  • Does `adb shell setprop debug.assert 1` turn on asserts globally (in all apps)? Is this setting retained on device restart? – Merk Sep 01 '13 at 00:36
9

If you're concerned about shipping code with the JUnit asserts in (or any other class path), you can use the ProGuard config option 'assumenosideeffects', which will strip out a class path on the assumption that removing it does nothing to the code.

Eg.

-assumenosideeffects class junit.framework.Assert {
*;
}

I have a common debug library I put all my testing methods in, and then use this option to strip it from my released apps.

This also removes the hard to spot problem of strings being manipulated that are never used in release code. For example if you write a debug log method, and in that method you check for debug mode before logging the string, you are still constructing the string, allocating memory, calling the method, but then opting to do nothing. Stripping the class out then removes the calls entirely, meaning as long as your string is constructed inside the method call, it goes away as well.

Make sure it is genuinely safe to just strip the lines out however, as it is done with no checking on ProGuard's part. Removing any void returning method will be fine, however if you are taking any return values from whatever you are removing, make sure you aren't using them for actual operational logic.

Grzegorz Adam Hankiewicz
  • 7,349
  • 1
  • 36
  • 78
Zulaxia
  • 2,702
  • 2
  • 22
  • 23
1

I mean if you were using a language feature, like assert(), the compiler should be able to strip that out. But this is an actual class and if a class is referenced by executable code it will be included or assumed included in the final product by the compiler.

However there is nothing stopping you from creating a script that removes all the references to the Assert class in all of your code base before compilation.

Another approach would be to make a test project that targets your application and within JUnit tests actually calls the Assert on the areas which you want to make sure work. I personally like this approach because it is a nice and clean separation of test and application.

If you are just worried about the having an if-statement everywhere, then just wrap Assert with your own class, DebuggableAssert which does that check before each call to Assert.X. It will be sort of less performant because of the method entry/exit and the conditionals but if you can maintain your code better then it might be worth it.

Greg Giacovelli
  • 10,164
  • 2
  • 47
  • 64
  • Okay after reading a bit more about this stuff I decided to use the Java language assert. In the debug version I want to break into the debugger when an assert fails. And I want to have all the asserts disabled in the release version. To get this to work in Eclipse I did the following: Added exception breakpoints to AssertionError (caught and uncaught) in eclipse I turned assertions on by Windows/Preference/Java/Installed JREs/(edit the JRE)/set default arguments to -ea – jbww Feb 18 '11 at 20:43
  • I must still be missing some step because when I try to test it by causing an assert to fail, nothing happens. The program just runs through the assert. So this makes me believe that the Java language asserts are not really enabled, otherwise either I would break on the exception as intended or if I did not set the breakpoint correctly the uncaught exception would halt the program. Any idea what additional step I am missing? I am running my IDE on Windows. thanks – jbww Feb 18 '11 at 20:43
  • Oh no I didn't mean to lead you on to using that. I was saying that's how normal java would approach this. I beleive the dex compiler will strip out the assert keyword. Usually the javac program always does unless a flag is enabled. I was just saying why there was going to be the use of the class since you had it in your code and a language keyword would be able to be stripped by the compuler. Sorry wasn't saying use it. – Greg Giacovelli Feb 18 '11 at 22:15
  • Actually I did understand you. It just seemed that the simplest and cleanest aproach would be to use the java lang version of assert so that is what I am trying to do. It still leaves me with the problem that I am missing some step or steps that will enable assert. – jbww Feb 18 '11 at 23:36
  • Ah ok. Yeah I think Android strips out keyword assert in the runtime. So this solution won't work in Android, at least I don't think. – Greg Giacovelli Feb 19 '11 at 00:34