9

问题

我有两个要测试的 Android 类:

我目前有两个测试课程:

  • CommentContentProviderTest, 它扩展ProviderTestCase2<CommentContentProvider>并使用了MockContentResolver. 这工作正常。
  • CommentActivityTest, 延伸ActivityInstrumentationTestCase2<CommentActivity>. 这工作正常,除了CommentActivity那个 access的部分CommentContentProvider

问题是,当CommentActivity访问时CommentContentProvider,它是通过标准来完成的ContentResolver

ContentResolver resolver = getContentResolver();
Cursor cursor = resolver().query(...);

因此,当CommentActivityTest运行时,它会启动CommentActivity,它访问(读取和写入)生产数据库,如上两行所示。

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

相关问题

可能的解决方案

如果我可以通过 start 的 Intent 注入 a (可能是 a 或 )会很好,ContentResolverMockContentResolver不能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();
        }
        :
    }

我不喜欢这个选项,因为我不喜欢将与测试相关的代码放在我的非测试类中。

选项 2

使用抽象工厂模式传递一个Parcelable提供实数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);
        :
    }

我也有:

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,在测试中我传入一个实例MockContentResolverFactory。由于两者都没有任何状态,因此它们很容易 Parcelable/Serializable。

我对这种方法的担忧是,我不想成为在存在更简单的方法时过度使用设计模式的“那个人” 。

选项 3

将以下方法添加到CommentActivity

public void static setContentResolver(ContentResolver) {
  :
}

这比选项 1 更干净,因为它将创建的ContentResolver外部CommentActivity放在 .

选项 4

CommentActivityTestextendActivityUnitTestCase<CommentActivity>而不是 ActivityInstrumentationTestCase2<CommentActivity>. 这让我可以CommentActivity通过setActivityContext(). 我传递的上下文覆盖了通常getContentResolver()使用的 a MockContentResolver(我在其他地方初始化)。

private class MyContext extends RenamingDelegatingContext {
    MyContext(Context context) {
        super(context, FILE_PREFIX);
    }

    @Override
    public ContentResolver getContentResolver() {
        return mResolver;
    }
}

这有效,不需要修改被测类,但增加了更多复杂性,因为ActivityUnitTestCase<CommentActivity>.startActivity()不能在setUp() 方法中调用,每个 API

另一个不便之处是必须在触摸模式下测试活动,并且setActivityInitialTouchMode(boolean)定义在ActivityInstrumentationTestCase2<T>但不是ActivityUnitTestCase<T>

FWIW,我对正确处理这一点有点着迷,因为我将在我正在教授的 Android 开发课程中展示它。

4

1 回答 1

1

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

于 2014-03-30T01:35:04.483 回答