问题
我有两个要测试的Android类:
CommentContentProvider
,它扩展了 ContentProvider
并由SQLiteDatabase支持。 CommentActivity
,它扩展了 Activity
并通过 CommentContentProvider
间接访问ContentResolver
。 我目前有两个测试类(class):
CommentContentProviderTest
,它扩展了ProviderTestCase2<CommentContentProvider>
并使用了 MockContentResolver
。这很好。 CommentActivityTest
,扩展了ActivityInstrumentationTestCase2<CommentActivity>
。除CommentActivity
的可访问CommentContentProvider
的部分外,此方法均正常运行。 问题在于,当
CommentActivity
访问CommentContentProvider
时,它是通过标准ContentResolver
进行访问的:ContentResolver resolver = getContentResolver();
Cursor cursor = resolver().query(...);
因此,运行
CommentActivityTest
时,它将启动CommentActivity
,该访问和访问生产数据库,如以上两行所示。我的问题是如何使
CommentActivity
在生产中使用标准的ContentResolver
,但在测试过程中使用MockContentResolver
。相关问题
Activity
的类。 可能的解决方案
如果可以通过以
ContentResolver
开头的Intent注入(inject)MockContentResolver
(可能是RenamingDelegatingContext
或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
或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;
}
}
在生产中,我(通过 Intent )传入了
RealContentResolverFactory
实例,在测试中,我传入了MockContentResolverFactory
实例。由于两者都没有任何状态,因此它们很容易被打包/可序列化。我对这种方法的担心是,我不想成为"that guy"在存在更简单的方法时过度使用设计模式。
选项3
将以下方法添加到
CommentActivity
:public void static setContentResolver(ContentResolver) {
:
}
这比Option 1干净,因为它将
ContentResolver
的创建放在CommentActivity
之外,但是,像Option 1一样,它需要修改被测类。选项4
让
CommentActivityTest
扩展ActivityUnitTestCase<CommentActivity>
而不是ActivityInstrumentationTestCase2<CommentActivity>
。这使我可以通过 CommentActivity
设置setActivityContext()
的上下文。我通过的上下文将覆盖通常的getContentResolver()
以使用MockContentResolver
(在其他地方初始化)。private class MyContext extends RenamingDelegatingContext {
MyContext(Context context) {
super(context, FILE_PREFIX);
}
@Override
public ContentResolver getContentResolver() {
return mResolver;
}
}
这是可行的,并且不需要修改被测类,但是由于
ActivityUnitTestCase<CommentActivity>.startActivity()
cannot be called in the setUp(
) method, per the API,因此增加了更多的复杂性。另一个不便之处是必须在触摸模式下测试 Activity ,并且setActivityInitialTouchMode(boolean)是在
ActivityInstrumentationTestCase2<T>
中定义的,但不是在ActivityUnitTestCase<T>
中定义的。FWIW,我对实现这一目标有些痴迷,因为我将在我正在教授的Android开发类(class)中进行介绍。
最佳答案
选项2对我来说似乎最好。我不担心使用工厂。我对远距离引起行为改变的 Intent 感到不安。但是其他解决方案将非生产代码放入生产代码中,因此您要测试的内容与生产中的工作方式不太相似。希望能有所帮助。