如何在不影响生产数据库的情况下测试使用ContentProvider的Activity?

时间:2014-03-22 23:31:26

标签: android dependency-injection android-testing android-contentresolver abstract-factory

问题

我想测试两个Android类:

我目前有两个测试类:

  • CommentContentProviderTest,其扩展ProviderTestCase2<CommentContentProvider>并使用MockContentResolver。这很好。
  • CommentActivityTest,其中包含ActivityInstrumentationTestCase2<CommentActivity>。这项工作正常,但访问CommentActivity的{​​{1}}部分除外。

问题在于,当CommentContentProvider访问CommentActivity时,它会通过标准CommentContentProvider进行访问:

ContentResolver

因此,当ContentResolver resolver = getContentResolver(); Cursor cursor = resolver().query(...); 运行时,它会启动CommentActivityTest,它访问(读取和写入)生产数据库,如上面两行所示。

我的问题是如何让CommentActivity在生产中使用标准CommentActivity,但在测试期间使用ContentResolver

相关问题

可能的解决方案

如果我可以通过Activity ContentResolver注入一个MockContentResolver(可能是RenamingDelegatingContextCommentActivity),那会很好,但我可以&这样做,因为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(); } : } 类,该类提供真实的ParcelableContentProvider

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开发课程中进行演示。

1 个答案:

答案 0 :(得分:1)

选项2对我来说似乎最好。我对工厂的使用并不感到烦恼;我更加担心导致远距离行为改变的意图。但是其他解决方案将非生产代码放在生产代码中,因此您所测试的内容与生产中的工作方式不同。希望有所帮助。