我想测试一个与Bundles有关的方法。然而在测试环境中,我无法创建一个(非空)Bundle对象。
给定以下代码:
Bundle bundle = new Bundle();
bundle.putString("key", "value");
boolean containsKey = bundle.containsKey("key");
containsKey
是在应用程序上下文中执行时为 true
,但在单元测试中执行时为 false
。
我想测试一个与Bundles有关的方法。然而在测试环境中,我无法创建一个(非空)Bundle对象。
给定以下代码:
Bundle bundle = new Bundle();
bundle.putString("key", "value");
boolean containsKey = bundle.containsKey("key");
containsKey
是在应用程序上下文中执行时为 true
,但在单元测试中执行时为 false
。
If your build script contains something like this:
testOptions {
unitTests.returnDefaultValues = true
}
这就是为什么即使您没有为Bundle类指定mock,您的测试也不会失败的原因。
有几种方法可以解决这个问题:
使用Mockito mocking框架来mock Bundle类。不幸的是,您必须自己编写大量样板代码。例如,您可以使用此方法mock一个bundle对象,以便它通过getString方法返回正确的值:
@NonNull
private Bundle mockBundle() {
final Map<String, String> fakeBundle = new HashMap<>();
Bundle bundle = mock(Bundle.class);
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] arguments = invocation.getArguments();
String key = ((String) arguments[0]);
String value = ((String) arguments[1]);
fakeBundle.put(key, value);
return null;
}
}).when(bundle).putString(anyString(), anyString());
when(bundle.get(anyString())).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
Object[] arguments = invocation.getArguments();
String key = ((String) arguments[0]);
return fakeBundle.get(key);
}
});
return bundle;
}
使用 Robolectric 框架,它提供了一些用于单元测试的影子类。这允许您在单元测试中使用 Android 特定的类,并且它们将正常工作。通过使用该框架,您的单元测试几乎不需要进行任何更改即可正常运行。
这可能是你最不想听到的建议,但也是一个可行的方法。您可以使您的测试功能化并在您的 Android 设备或模拟器上运行。我不建议这种方式,因为速度很慢。在执行测试之前,您必须构建一个测试 apk、安装它并运行。如果您要进行 TDD,这非常缓慢。
@Override
public Set answer(InvocationOnMock invocation) {
return fakeBundle.keySet();
}
});`,以防您想测试迭代包的键集的内容。
- Nom1fan
Bundle mockBundle = Mockito.mock(Bundle.class);
Mockito.when(mockBundle.containsKey("key")).thenReturn(true);
containsKey现在将在您正在测试的类中为true。
关于when()的一些额外信息。当您使用when()包装模拟对象方法调用时,它将跳过调用该方法的实际实现,并立即返回thenReturn()部分提供的值。这对编写某些单元测试非常有用,因为一旦一个方法调用进入其兔子洞中,就可能会变成噩梦。
这是我发现的设置通往您实际想要测试的代码路径的最佳方式。
Bundle extras = mock(Bundle.class);
由于我不需要测试在bundle中传递的许多参数,所以我像这样模拟对它们的调用:
private void stubParameterNameExtras(Bundle extras, boolean value) {
when(extras.getBoolean(KEY, false)).thenReturn(value);
}
以下是一种使用Kotlin和mockk
的方法,模仿bundleOf
:
private fun mockBundleOf(vararg pairs: Pair<String, Any?>): Bundle {
val bundle = mockk<Bundle>()
for ((key, value) in pairs) {
when (value) {
is Boolean -> every { bundle.getBoolean(key) } returns value
is Byte -> every { bundle.getByte(key) } returns value
is Char -> every { bundle.getChar(key) } returns value
is Double -> every { bundle.getDouble(key) } returns value
is Float -> every { bundle.getFloat(key) } returns value
is Int -> every { bundle.getInt(key) } returns value
is Long -> every { bundle.getLong(key) } returns value
is Short -> every { bundle.getShort(key) } returns value
is String -> every { bundle.getString(key) } returns value
else -> throw UnsupportedOperationException("Type is not supported.")
}
}
return bundle
}