简体   繁体   中英

Android Base64 encode and decode return null in Unit Test

I am attempting to decode a Base64 encoded string in Android using thehttp://developer.android.com/reference/android/util/Base64.html class.

Both the encodeToString and decode methods are returning null, and I have no idea what's wrong, here's my code for the decode:

// Should decode to "GRC"
String friendlyNameBase64Encoded = "R1JD";

// This returns null
byte[] friendlyNameByteArray = Base64.decode(friendlyNameBase64Encoded, Base64.DEFAULT);

// Fails with NullPointerException
String friendlyName = new String(friendlyNameByteArray, "UTF-8");

I'm running Android API 23.1.0

I had the same problem in my unit tests. I didn't realize the Base64 class I'm using is a part of the Android API, therefore

You can't use android.util.Base64 in a regular JUnit test, it must be an Instrumentation test.

However, if you really want it as a unit test, you could use the Apache Commons Base64 class instead. Include it in Gradle build:

// https://mvnrepository.com/artifact/org.apache.commons/commons-collections4
compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.1'

And then slightly different usage,

Follow up o android tutorials and unit test notes while you need just Unit tests without use of some android libs

In your case you're depending on android.Base64. I had similar issue and moving test classes from src/test -> src/androidTest worked. Those tests are run on virtual machine or real android device. I didn't notice the diff at the first look.

You can use the Robolectric Runner

  1. Add the dependency in your build.gradle :

     testCompile 'org.robolectric:robolectric:XXX' 
  2. Add this line in your testing class:

     import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class MyTestingClassTest { ... } 

As discussed, android.util.Base64.decode is returning a null in test harness because of this setting in the build file:

testOptions {
    unitTests.returnDefaultValues = true
}

To avoid including other libraries you could fall back on java.util.Base64, which is only available in Java8 and on Android 26 and above. If you already target 26+ then just switch to this method, but if you have to target earlier SDKs you could check for the null return and call a test-harness method instead:

// Required because Android classes return null in desktop unit tests
@TargetApi(26)
private fun testHarnessDecode(s : String) : ByteArray {
    return java.util.Base64.getDecoder().decode(s)
}

I would rather do this than pull in additional library dependencies, but YMMV.

What works with me is to mock Base64.decode method using mockk

 mockkStatic(Base64::class)
 every { Base64.decode(any<String>(),Base64.DEFAULT) } returns byteArrayOf()

What I ended up doing was a wrapper. I allow the android.util.Base64 to fail and then if we are on build greater than "O" or equal to 0 (zero. meaning not an android device or no android sdk version).

 class Base64Wrapper {
    companion object {
        fun encodeToString(input : ByteArray) : String {
            return try {
                Base64.encodeToString(input, Base64.DEFAULT)
            } catch (e :Exception) {
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O || android.os.Build.VERSION.SDK_INT == 0) {
                    java.util.Base64.getEncoder().encodeToString(input)
                } else {
                    throw e;
                }
            }
        }

        fun decode(input: String) : ByteArray {
            return try {
                Base64.decode(input, Base64.DEFAULT)
            } catch (e :Exception) {
                // if build is greater or equal to "O" or is equal to 0 (zero) which means it is not a device (i.e. unit test).
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O || android.os.Build.VERSION.SDK_INT == 0) {
                    java.util.Base64.getDecoder().decode(input)
                } else {
                    throw e;
                }
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM