如何在安卓中模拟 Base64?

17

我正在为一个使用 android.util.Base64 的类编写单元测试,但是我遇到了这个错误:

java.lang.RuntimeException: Method encode in android.util.Base64 not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.util.Base64.encode(Base64.java)

这是使用encode()方法的代码:

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// [write some data to the stream]
byte[] base64Bytes = Base64.encode(byteArrayOutputStream.toByteArray(), Base64.DEFAULT);

现在我明白了我不能在我的单元测试中使用Android库类。但是如何正确地模拟Base64,以便我可以为我的类编写正确的单元测试呢?


为什么需要模拟它?那个函数是完全确定性的,因此您可以编写测试以检查您实际感兴趣的其他属性。 - Emil L
1
建议将工具封装在一个抽象层后面,并将其明确注入到测试主题中,以便进行模拟。 - Nkosi
2
我建议与@Nkosi相同。如果您不想这样做,可以使用PowerMock来模拟静态函数。 - Christopher
1
找到了这个,也支持@Christopher的建议 https://dev59.com/bVkS5IYBdhLWcg3w1JhK#39406002 - Nkosi
我会尝试使用PowerMock,谢谢你的建议。 - O.O.Balance
显示剩余3条评论
5个回答

30

我迟到了,但也许这能帮助某些人。 对于 JUnit,你可以模拟类而无需任何第三方库。只需在 app/src/test/java/android/util 中创建一个名为 Base64.java 的文件,并写入以下内容:

package android.util; 

public class Base64 {

    public static String encodeToString(byte[] input, int flags) {
        return java.util.Base64.getEncoder().encodeToString(input);
    }

    public static byte[] decode(String str, int flags) {
        return java.util.Base64.getDecoder().decode(str);
    }

    // add other methods if required...
}

enter image description here


1
在我的 Kotlin 项目中,我不得不在文件的顶部添加 @file:JvmName("Base64"),以便它能够被正确地识别。 - simne7
请分享一些更多的实现细节。 - Naveen Kumar
1
@NaveenKumar,你只需要创建一个名为Base64.java的文件,然后将其放在该目录下,查看我添加的截图即可。 - ruchkin
1
我直接从这里复制了Base64.java的源代码:https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/util/Base64.java,你的版本对于我的使用情况来说有点太简单了。 - Alberto M

8

根据Nkosi和Christopher的评论,我找到了一个解决方案。我使用PowerMock模拟了Base64的静态方法:

PowerMockito.mockStatic(Base64.class);
when(Base64.encode(any(), anyInt())).thenAnswer(invocation -> java.util.Base64.getEncoder().encode((byte[]) invocation.getArguments()[0]));
when(Base64.decode(anyString(), anyInt())).thenAnswer(invocation -> java.util.Base64.getMimeDecoder().decode((String) invocation.getArguments()[0]));

在我的build.gradle中,我需要添加以下内容:
testImplementation "org.powermock:powermock-module-junit4:1.7.4"
testImplementation "org.powermock:powermock-api-mockito2:1.7.4"

请注意,不是每个Powermock版本都与每个Mockito版本兼容。我在这里使用的版本应该与Mockito 2.8.0-2.8.9兼容,并且没有任何问题。然而,对于Mockito 2的支持仍处于实验阶段。在项目的wiki中有一张详细列出兼容版本的表格。

我按照这种方法操作,但在DefaultMockHandler中出现了错误,可能是由于mockito的版本不兼容。请问您在依赖项中使用的是哪个版本的mockito? - Peter F
1
@PeterF 版本2.8.9:testImplementation 'org.mockito:mockito-core:2.8.9' - O.O.Balance
1
我尝试了各种不同的版本配置,但它们都无法很好地配合。现在我使用以下依赖: testImplementation "org.mockito:mockito-core:2.23.0" testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.0-beta.5' testImplementation 'org.powermock:powermock-core:2.0.0-beta.5' testImplementation 'org.powermock:powermock-module-junit4:2.0.0-beta.5' testImplementation 'org.powermock:powermock-api-mockito2:2.0.0-beta.5' - PhilLab
是的,兼容性有点脆弱。我已经在我的回答中添加了一个指向Github版本表的链接。感谢分享你的配置,我自己还没有尝试过这个beta版。 - O.O.Balance

3

使用最新版本的Mockito,您也可以模拟静态方法。不再需要powermockito:

在gradle中:

testImplementation "org.mockito:mockito-inline:4.0.0"
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"

使用 Kotlin:

mockStatic(Base64::class.java)
`when`(Base64.encode(any(), anyInt())).thenAnswer { invocation ->
    java.util.Base64.getMimeEncoder().encode(invocation.arguments[0] as ByteArray)
}
`when`(Base64.decode(anyString(), anyInt())).thenAnswer { invocation ->
    java.util.Base64.getMimeDecoder().decode(invocation.arguments[0] as String)
}

2

对于Kotlin,您可以使用以下方法:

package android.util

import java.util.Base64


public object Base64 {
    @JvmStatic
    public fun encodeToString(input: ByteArray?, flags: Int): String {
        return Base64.getEncoder().encodeToString(input)
    }

    @JvmStatic
    public fun decode(str: String?, flags: Int): ByteArray {
        return Base64.getDecoder().decode(str)
    }
}

这个应该放在哪里? - behelit
你可以在src/test源集中创建一个android.util包。(可以在app/src/test/java/android/util中) - Nico

0

如果你运行一个调用Android SDK中未模拟的API的测试,你会收到一个错误提示,说这个方法没有被模拟。这是因为用于运行单元测试的android.jar文件不包含任何实际代码(这些API仅由设备上的Android系统映像提供)。请使用java Base64.getDecoder().decode()替换。


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