如何在FragmentDialog和实现接口的Activity之间进行单元测试交互

时间:2015-08-20 10:58:12

标签: android mockito robolectric android-testing

我有MainActivity显示FragmentDialog(EditIntervalFragment)以捕获用户的输入。 Activity实现EditIntervalListener接口。在onAtach方法片段中,将活动转换为EditIntervalListener。 我想测试我的EditIntervalFragment正确调用EditIntervalListener方法并使用正确的参数。

我最初的意图是使用Roblectric和Mockito。以下代码几乎可以使用。

@Test
public void shouldCallInterfaceAfterModify() {
    MainActivity hostActivity = Robolectric.setupActivity(MainActivity.class);
    EditIntervalFragment editIntervalFragment = EditIntervalFragment.getInstance(0, TEST_NAME, TEST_DURATION);
    editIntervalFragment.show(hostActivity.getSupportFragmentManager(), "test");
    AlertDialog dialog = (AlertDialog) editIntervalFragment.getDialog();
    assertNotNull(dialog);

    EditIntervalFragment.EditIntervalListener activity = Mockito.spy(hostActivity);
    dialog.findViewById(android.R.id.button1).performClick();
    verify(activity).onIntervalChanged(0,TEST_NAME,TEST_DURATION);
}

此代码使用真实MainActivity的问题。这意味着将执行所有MainActivity的逻辑。我想避免这种情况。我怎么能这样做?

更新 我找到了一种不称为真MainActivity的方法。我创建了另一个活动,仅供测试。

public class ActivityTest extends FragmentActivity implements EditIntervalFragment.EditIntervalListener {

    //empty methods here
}

我的测试现在看起来像这样

@Test
public void shouldCallInterfaceAfterModify() {
    ActivityTest hostActivity = Robolectric.setupActivity(ActivityTest.class);
    ActivityTest spy = Mockito.spy(hostActivity);
    EditIntervalFragment editIntervalFragment = EditIntervalFragment.getInstance(0, TEST_NAME, TEST_DURATION);
    editIntervalFragment.show(spy.getSupportFragmentManager(), "test");
    AlertDialog dialog = (AlertDialog) editIntervalFragment.getDialog();
    assertNotNull(dialog);

    dialog.findViewById(android.R.id.button1).performClick();
    verify(spy).onIntervalChanged(0, TEST_NAME, TEST_DURATION);
}

但是在测试执行后,我收到错误说只有spy.getSupportFragmentManager()被调用。我完全确定应该调用onIntervalChanged

寻求帮助。我该如何实施这种测试?

2 个答案:

答案 0 :(得分:1)

我最终得到了这个解决方案。创建一个实现接口的活动,跟踪所有交互。

public class ActivityTest extends FragmentActivity implements EditIntervalFragment.EditIntervalListener {

    public int mIntervalChangedCalls = 0;
    public int mPosition;
    public String mName;
    public long mDurationMillSec;

    @Override
    public void onIntervalChanged(int position, String name, long durationMillSec) {
        mIntervalChangedCalls++;
        mPosition = position;
        mName = name;
        mDurationMillSec = durationMillSec;
    }
}

我的测试看起来像这样

@Test
public void shouldCallOnIntervalChanged() {
    ActivityTest hostActivity = Robolectric.setupActivity(ActivityTest.class);
    EditIntervalFragment editIntervalFragment = EditIntervalFragment.getInstance(0, TEST_NAME, TEST_DURATION);
    editIntervalFragment.show(hostActivity.getSupportFragmentManager(), "test");
    AlertDialog dialog = (AlertDialog) editIntervalFragment.getDialog();
    assertNotNull(dialog);

    dialog.findViewById(android.R.id.button1).performClick();
    assertThat(hostActivity.mIntervalChangedCalls).isEqualTo(1);
    assertThat(hostActivity.mPosition).isEqualTo(0);
    assertThat(hostActivity.mName).isEqualTo(TEST_NAME);
    assertThat(hostActivity.mDurationMillSec).isEqualTo(TEST_DURATION);
}

对于仅为测试目的而创建单独的类,我并不完全满意。我想Mockito或Robolectric可以实现同样的效果,但我不知道如何。

所以我仍然愿意接受任何想法或建议。如果没有人在一周内提供更好的解决方案,我会接受我自己的答案。

答案 1 :(得分:1)

当你不控制生命周期时,总是挑战工作间谍。

我们通常在做什么,我们将所有不相关的功能提取到实用程序类并在测试中模拟它们。它还有助于设计应用程序(单一类责任规则)。

当然,这取决于你是否对这些数据做了些什么。如果它只是数据类而不是Factory来创建这个数据类并再次在测试中模拟它。所有这些都需要适当的DI(看看Dagger)。

您的方法没有任何问题,但它并没有强迫您将您的应用视为彼此互动的小部件。但与此同时,它带来了更多的复杂性,以后会得到回报