Android单元测试中Base64编码和解码返回null

25

我正试图在Android中使用http://developer.android.com/reference/android/util/Base64.html类解码一个Base64编码的字符串。

无论是encodeToString还是decode方法都返回null,我不知道出了什么问题,以下是我的解码代码:

// 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");

我正在运行Android API 23.1.0


1
请问您能否发布logcat消息?顺便说一下,我成功地从您发布的代码中获取了解码后的字符串,即GRC。 - Suhas
到目前为止,我只是从单元测试中运行这段代码。我刚刚真正地运行了应用程序,它似乎正在工作。你有什么想法,单元测试可能出了什么问题吗? - brnby
我的理解是,通常情况下,在单元测试期间,Android API 调用会抛出异常,将 returnDefaultValues 设置为 true 可以使它们表现正常,那么这种差异是为什么呢? - brnby
3
不完全确定,但我猜单元测试无法访问 android.util.Base64 类,因此返回了 null。也许这个链接能帮到你 http://tools.android.com/tech-docs/unit-testing-support。 - Suhas
3
根据我所读的,它实际上只会导致API调用返回null或零,而不是抛出异常。我想我必须将其编写为仪表测试,感谢您的帮助! - brnby
显示剩余4条评论
6个回答

30

在我的单元测试中遇到了同样的问题。我没有意识到我正在使用的Base64类是Android API的一部分,因此

您无法在常规的JUnit测试中使用android.util.Base64,它必须是Instrumentation测试。

但是,如果你真的想要将其作为单元测试,你可以使用Apache Commons Base64类代替。在Gradle build中包含它:

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

然后稍微不同的用法,


编译组:'commons-codec',名称:'commons-codec',版本:'1.9'是对我有效的依赖项。 - hetptis
二进制的咒骂 - undefined

9
您可以使用Robolectric Runner。
  1. Add the dependency in your build.gradle:

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

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

这是正确的做法。即使在2023年,这也应该是被接受的答案! - anemo

5

如之前所讨论的那样,android.util.Base64.decode在测试工具中返回null,因为build文件中有这个设置:

testOptions {
    unitTests.returnDefaultValues = true
}

为避免包含其他库,您可以使用java.util.Base64,它仅适用于Java8及Android 26及以上版本。如果您的目标已经是26+,则只需切换到此方法,但如果您必须针对早期的SDK,则可以检查null返回并调用测试支持方法。
// 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)
}

我宁愿这样做,而不引入其他库的依赖关系,但你的情况可能会有所不同。


1
java.util.Base64.getEncoder().encode(byteArray) 返回 null,即使 encode 返回了一些东西;但是,如果我使用 encode(byteArray).toString(),它是一个无效的 Base64 字符串,无法解码。 - Zachary Sweigart

5

当你只需要进行单元测试而不使用一些android库时,可以跟随android教程和单元测试笔记。

在您的情况下,您依赖于android.Base64。我曾遇到过类似的问题,将测试类从src/test移动到src/androidTest即可解决。这些测试在虚拟机或真实的android设备上运行。我一开始没有注意到区别。


很棒的答案,但我只是在androidTest中简单编写了新测试。 - Fortran

2

我使用mockk来模拟Base64.decode方法

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

0
我最终做的是一个包装器。我允许android.util.Base64失败,然后如果我们的构建版本大于“O”或等于0(零。意味着不是Android设备或没有Android SDK版本)。
 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;
                }
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接