如何让我的 activity 使用测试数据?

How do I make my activity use testing data?

我有一个显示来自网络的数据(帖子)的应用程序 API。

我目前主要测试activity如下

@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);

@Test
public void testDataLoad() {
    int postsTotal = DataSingleton.getInstance().getPostsCount();
    ViewInteraction empty = onView(withId(R.id.empty_view));
    ViewInteraction recycler = onView(withId(R.id.recycler_view));
    if (postsTotal == 0) {
        empty.check(matches(isDisplayed()));
        recycler.check(matches(not(isDisplayed())));
    } else {
        empty.check(matches(not(isDisplayed())));
        recycler.check(matches(isDisplayed()));
        recycler.check(new RecyclerViewItemCountAssertion(greaterThan(postsTotal)));
    }
}

我知道这不是编写测试的正确方法。我希望能够同时使用空数据集和非空数据集进行测试,以便 if-else 是两个单独的测试。我认为我能实现它的唯一方法是模拟数据。

还有别的办法吗? 我可以使用 Mockito 让 MainActivity 使用模拟数据而不修改生产代码吗?我唯一的选择是让它注入真实的或模拟的数据提供者来代替我的单身人士吗?

是否每次只卸载并重新安装我的应用程序,这样就没有数据可以开始,然后继续进行真实数据测试?

Android Activity 是重量级的,很难测试。因为我们无法控制构造函数,所以很难交换测试替身。

首先要做的是确保您依赖于数据源的抽象而不是具体化。因此,如果您使用带有 getPostsCount() 方法的单例,则提取一个接口:

interface DataSourceAbstraction {
    int getPostsCount();
}

制作一个实现您的接口的包装器 class:

class ConcreteDataSource implements DataSourceAbstraction {
    @Override
    int getPostsCount() {
        return DataSingleton.getInstance().getPostsCount();
    }
}

并使 Activity 依赖于它而不是具体的 DataSingleton

DataSourceAbstraction dataSourceAbstraction;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super(savedInstanceState);
    injectMembers();
}

@VisibleForTesting
void injectMembers() {
    dataSourceAbstraction = new ConcreteDataSource();
}

您现在可以通过子classing 和覆盖具有放宽可见性的 injectMembers 来交换测试替身。在企业开发中这样做是个坏主意,但在 Android 活动中您不控制 class.

的构造函数的选项较少

您现在可以写:

DataSourceAbstraction dataSource;

//system under test
MainActivity mainActivity

@Before
public void setUp() {
    mockDataSource = Mockito.mock(DataSourceAbstraction.class);
    mainActivity = new MainActivity() {
        @Override
        void injectMembers() {
            dataSourceAbstraction = mockDataSource;
        }
    };
}