Android 如何单元测试FragmentDialog和实现接口的活动之间的交互

Android 如何单元测试FragmentDialog和实现接口的活动之间的交互,android,mockito,robolectric,android-testing,Android,Mockito,Robolectric,Android Testing,我有MainActivity,它显示FragmentDialog(EditIntervalFragment)以捕获用户的输入。活动实现EditIntervalListener接口。在onAtach方法中,片段将活动强制转换为EditIntervalListener。 我想测试我的EditIntervalFragment是否使用正确的参数正确调用EditIntervalListener方法 我最初的意图是使用Robelect和Mockito。下面的代码几乎可以工作 @Test public voi

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

我最初的意图是使用Robelect和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);
}
此代码的问题在于它使用real
MainActivity
。这意味着将执行所有
main活动
的逻辑。我想避免这种情况。我该怎么做

更新 我找到了一种不调用real
main活动的方法。我创建了另一个活动,只是为了测试

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);
}
@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);
}
但在测试执行之后,我收到一条错误消息,表示只调用了
spy.getSupportFragmentManager()
。我100%确定应该调用
onIntervalChanged


正在寻求帮助。如何实现这种测试?

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

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 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);
}
@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也可以做到这一点,但我不知道如何做到


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

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

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

当然,这取决于你是否对这些数据做了些什么。若它只是一个数据类,那个么我将使用
Factory
来创建这个数据类,并在测试中再次模拟它。所有这些都需要适当的DI(注视匕首)


你的方法没有错,但它不会强迫你把你的应用程序看作是相互作用的小部分。但同时它也带来了更多的复杂性,以后会有回报的

谢谢。我被这种
onAtach-getActivity-cast-to-listener
方法困住了。主要是因为我到处都能看到它(甚至AndroidStudio也会生成这样的片段)。现在,我的对话有了一个专门的演示者。与活动没有任何联系。我喜欢:)