Ell*_*tus 9 android dependency-injection abstract-factory android-contentresolver android-testing
问题
我有两个我要测试的Android类:
CommentContentProvider,它扩展ContentProvider并由SQLiteDatabase支持.CommentActivity,通过一个间接扩展Activity和访问.CommentContentProviderContentResolver我目前有两个测试类:
CommentContentProviderTest,扩展ProviderTestCase2<CommentContentProvider>和使用MockContentResolver.这很好用.CommentActivityTest,延伸ActivityInstrumentationTestCase2<CommentActivity>.除了CommentActivity访问的部分外,这种方法很好CommentContentProvider.问题是,当CommentActivity访问时CommentContentProvider,它通过标准这样做ContentResolver:
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver().query(...);
Run Code Online (Sandbox Code Playgroud)
因此,当CommentActivityTest运行时,它会启动CommentActivity,访问(读取和写入)生产数据库,如上面两行所示.
我的问题是如何在生产中CommentActivity使用标准ContentResolver,但MockContentResolver在测试期间.
相关问题
Activity.可能的解决方案
如果我可以通过启动的Intent注入ContentResolver(可能是a MockContentResolver或RenamingDelegatingContext),那将是很好的,但我不能这样做,因为s不是.CommentActivityContextParcelable
以下哪个选项最好,还是有更好的选择?
选项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();
}
:
}
Run Code Online (Sandbox Code Playgroud)
我不喜欢这个选项,因为我不喜欢将测试相关的代码放在我的非测试类中.
方案2
使用抽象工厂模式传递一个Parcelable提供real 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);
:
}
Run Code Online (Sandbox Code Playgroud)
在哪里我也有:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
在生产中,我传入(通过意图)一个实例RealContentResolverFactory,并在测试中传递一个实例MockContentResolverFactory.由于两者都没有任何状态,因此它们很容易Parcelable/Serializable.
我对这种方法的关注是,当存在更简单的方法时,我不想成为过度使用设计模式的"那个人".
方案3
将以下方法添加到CommentActivity:
public void static setContentResolver(ContentResolver) {
:
}
Run Code Online (Sandbox Code Playgroud)
这比选项1更清晰,因为它创建了ContentResolver外部CommentActivity,但是,与选项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;
}
}
Run Code Online (Sandbox Code Playgroud)
这有效,并且不需要修改被测试的类,但增加了更多的复杂性,因为根据API,ActivityUnitTestCase<CommentActivity>.startActivity()无法在setUp(方法中调用.
另一个不便之处是必须在触摸模式下测试活动,并且定义了setActivityInitialTouchMode(boolean)ActivityInstrumentationTestCase2<T>但不是ActivityUnitTestCase<T>.
FWIW,我对于正确实现这一点非常着迷,因为我将在我正在教授的Android开发课程中进行演示.
选项 2 对我来说似乎最好。我不关心工厂的使用;我更担心的是在远处导致行为改变的意图。但其他解决方案将非生产代码放入生产代码中,因此您正在测试的内容与生产中的工作方式不太相似。希望有帮助。
| 归档时间: |
|
| 查看次数: |
1208 次 |
| 最近记录: |