问题
我想测试两个Android类:
CommentContentProvider
,其扩展ContentProvider
并由SQLiteDatabase支持。CommentActivity
,其扩展Activity
并通过ContentResolver
间接访问CommentContentProvider
。我目前有两个测试类:
CommentContentProviderTest
,其扩展ProviderTestCase2<CommentContentProvider>
并使用MockContentResolver
。这很好。CommentActivityTest
,其中包含ActivityInstrumentationTestCase2<CommentActivity>
。这项工作正常,但访问CommentActivity
的{{1}}部分除外。问题在于,当CommentContentProvider
访问CommentActivity
时,它会通过标准CommentContentProvider
进行访问:
ContentResolver
因此,当ContentResolver resolver = getContentResolver();
Cursor cursor = resolver().query(...);
运行时,它会启动CommentActivityTest
,它访问(读取和写入)生产数据库,如上面两行所示。
我的问题是如何让CommentActivity
在生产中使用标准CommentActivity
,但在测试期间使用ContentResolver
。
相关问题
MockContentResolver
可能的解决方案
如果我可以通过Activity
ContentResolver
注入一个MockContentResolver
(可能是RenamingDelegatingContext
或CommentActivity
),那会很好,但我可以&这样做,因为Intent不是Context
。
以下哪个选项最好,还是有更好的选择?
选项1
向启动Intent
的{{1}}添加调试标记:
CommentActivity
我不喜欢这个选项,因为我不想将与测试相关的代码放在我的非测试类中。
选项2
使用抽象工厂模式传递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();
}
:
}
类,该类提供真实的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;
}
}
的实例,并在测试中传递RealContentResolverFactory
的实例。由于两者都没有任何状态,因此它们很容易Parcelable / Serializable。
我对这种方法的关注是,当存在更简单的方法时,我不想成为Parcelable
过度使用设计模式的人。
选项3
将以下方法添加到MockContentResolverFactory
:
CommentActivity
这比选项1更清晰,因为它将public void static setContentResolver(ContentResolver) {
:
}
的创建放在ContentResolver
之外,但是,与选项1一样,它需要修改被测试的类。
选项4
CommentActivity
延长CommentActivityTest
而不是ActivityUnitTestCase<CommentActivity>
。这样我就可以通过"that guy"设置ActivityInstrumentationTestCase2<CommentActivity>
的上下文了。我传递的上下文会覆盖通常的CommentActivity
以使用getContentResolver()
(我在其他地方初始化)。
MockContentResolver
这是有效的,不需要修改被测试的类,但由于setActivityContext()
,因此增加了更多的复杂性。
另一个不便之处是必须在触摸模式下测试活动,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,但ActivityInstrumentationTestCase2<T>
未定义。{/ p>
FWIW,我对于正确实现这一点非常着迷,因为我将在我正在教授的Android开发课程中进行演示。
答案 0 :(得分:1)
选项2对我来说似乎最好。我对工厂的使用并不感到烦恼;我更加担心导致远距离行为改变的意图。但是其他解决方案将非生产代码放在生产代码中,因此您所测试的内容与生产中的工作方式不同。希望有所帮助。