问题
我有两个要测试的 Android 类:
CommentContentProvider
,它扩展ContentProvider
并由SQLiteDatabase支持。CommentActivity
,它通过 a 间接扩展Activity
和访问。CommentContentProvider
ContentResolver
我目前有两个测试课程:
CommentContentProviderTest
, 它扩展ProviderTestCase2<CommentContentProvider>
并使用了MockContentResolver
. 这工作正常。CommentActivityTest
, 延伸ActivityInstrumentationTestCase2<CommentActivity>
. 这工作正常,除了CommentActivity
那个 access的部分CommentContentProvider
。
问题是,当CommentActivity
访问时CommentContentProvider
,它是通过标准来完成的ContentResolver
:
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver().query(...);
因此,当CommentActivityTest
运行时,它会启动CommentActivity
,它访问(读取和写入)生产数据库,如上两行所示。
我的问题是如何在生产中但在测试期间CommentActivity
使用标准。ContentResolver
MockContentResolver
相关问题
- 这与使用 ContentProviders 的Android 单元测试以及我发现的关于测试 ContentProviders 的其他问题不同,因为这些可以扩展为测试 ContentProviders 而设计的 android.test 类,而我需要扩展一个类来测试
Activity
. - 这类似于在没有第三方框架的情况下测试 Android 活动时如何注入依赖项?,这也是我发布的,但没有得到答复。如果有帮助,我现在愿意使用第三方框架。
- 使用 MockContentResolver 查询导致 NullPointerException相关并导致下面选项 1 中的解决方案,但我不知道它是否是我的最佳解决方案。
可能的解决方案
如果我可以通过 start 的 Intent 注入 a (可能是 a 或 )会很好,但ContentResolver
我MockContentResolver
不能RenamingDelegatingContext
这样做,因为s 不是。CommentActivity
Context
Parcelable
以下哪个选项是最好的,还是有更好的选择?
选项1
将调试标志添加到Intent
开始CommentActivity
:
public class CommentActivity extends Activity {
public static final String DEBUG_MODE = "DEBUG MODE";
private ContentResolver mResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
:
// If the flag is not present, debugMode will be set to false.
boolean debugMode = getIntent().getBooleanExtra(DEBUG_MODE, false);
if (debugMode) {
// Set up MockContentResolver or DelegatingContextResolver...
} else {
mResolver = getContentResolver();
}
:
}
我不喜欢这个选项,因为我不喜欢将与测试相关的代码放在我的非测试类中。
选项 2
使用抽象工厂模式传递一个Parcelable
提供实数ContentProvider
或 a 的类MockContentProvider
:
public class CommentActivity extends Activity {
public static final String FACTORY = "CONTENT RESOLVER FACTORY";
private ContentResolver mResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
:
ContentResolverFactory factory = getIntent().getParcelableExtra(FACTORY);
mResolver = factory.getContentResolver(this);
:
}
我也有:
public abstract class ContentResolverFactory implements Parcelable {
public abstract ContentResolver getContentResolver(Context context);
}
public abstract class RealContentResolverFactory extends ContentResolverFactory
public ContentResolver getContentResolver(Context context) {
return context.getContextResolver();
}
}
public abstract class MockContentResolverFactory extends ContentResolverFactory
public ContentResolver getContentResolver(Context context) {
MockContentResolver resolver = new MockContentResolver();
// Set up MockContentResolver...
return resolver;
}
}
在生产中,我(通过意图)传入一个实例RealContentResolverFactory
,在测试中我传入一个实例MockContentResolverFactory
。由于两者都没有任何状态,因此它们很容易 Parcelable/Serializable。
我对这种方法的担忧是,我不想成为在存在更简单的方法时过度使用设计模式的“那个人” 。
选项 3
将以下方法添加到CommentActivity
:
public void static setContentResolver(ContentResolver) {
:
}
这比选项 1 更干净,因为它将创建的ContentResolver
外部CommentActivity
放在 .
选项 4
有CommentActivityTest
extendActivityUnitTestCase<CommentActivity>
而不是 ActivityInstrumentationTestCase2<CommentActivity>
. 这让我可以CommentActivity
通过setActivityContext()
. 我传递的上下文覆盖了通常getContentResolver()
使用的 a MockContentResolver
(我在其他地方初始化)。
private class MyContext extends RenamingDelegatingContext {
MyContext(Context context) {
super(context, FILE_PREFIX);
}
@Override
public ContentResolver getContentResolver() {
return mResolver;
}
}
这有效,不需要修改被测类,但增加了更多复杂性,因为ActivityUnitTestCase<CommentActivity>.startActivity()
不能在setUp(
) 方法中调用,每个 API。
另一个不便之处是必须在触摸模式下测试活动,并且setActivityInitialTouchMode(boolean)定义在ActivityInstrumentationTestCase2<T>
但不是ActivityUnitTestCase<T>
。
FWIW,我对正确处理这一点有点着迷,因为我将在我正在教授的 Android 开发课程中展示它。