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

Ell*_*tus 9 android dependency-injection abstract-factory android-contentresolver android-testing

问题

我有两个我要测试的Android类:

我目前有两个测试类:

  • 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在测试期间.

相关问题

可能的解决方案

如果我可以通过启动的Intent注入ContentResolver(可能是a MockContentResolverRenamingDelegatingContext),那将是很好的,但我不能这样做,因为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开发课程中进行演示.

Nam*_*ter 1

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

  • 谢谢回复。沉默震耳欲聋。:-) (2认同)