如何对实现接口的 FragmentDialog 和 Activity 之间的交互进行单元测试
How to unit test interaction between FragmentDialog and Activity which implements interface
我有 MainActivity
显示 FragmentDialog (EditIntervalFragment
) 以捕获用户的输入。 Activity 实现了 EditIntervalListener
接口。在 onAtach
方法片段中,将 activity 转换为 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
的逻辑都会被执行。我想避免这种情况。我该怎么做?
更新
我找到了一种不调用 real MainActivity
的方法。我创建了另一个 activity,只是为了测试。
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()
被调用了。我 100% 确定应该调用 onIntervalChanged
。
寻求帮助。我怎样才能实施这种测试?
我最终得到了这个解决方案。创建一个 Activity 实现接口并跟踪所有交互。
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);
}
我对创建一个单独的 class 仅用于测试目的并不完全满意。我想同样可以用 Mockito 或 Robolectric 实现,但我不知道如何实现。
所以我仍然愿意接受任何想法或建议。如果一周内没有人给出更好的解决方案,我会接受我自己的答案。
当你不控制生命周期时,让工作成为间谍总是很有挑战性的。
我们通常做的是将所有不相关的功能提取到实用程序 classes 并在测试中模拟它们。它还有助于应用程序的设计(单一 class 责任规则)。
当然,这取决于您是否对这些数据进行处理。如果它只是数据 class,那么我将 Factory
用于创建此数据 classes 并再次在测试中模拟它。所有这些都需要适当的 DI(看 Dagger)。
您的方法没有任何问题,但它不会强迫您将您的应用程序视为相互交互的小部件。但同时它带来了更多的复杂性,稍后会得到回报
我有 MainActivity
显示 FragmentDialog (EditIntervalFragment
) 以捕获用户的输入。 Activity 实现了 EditIntervalListener
接口。在 onAtach
方法片段中,将 activity 转换为 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
的逻辑都会被执行。我想避免这种情况。我该怎么做?
更新
我找到了一种不调用 real MainActivity
的方法。我创建了另一个 activity,只是为了测试。
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()
被调用了。我 100% 确定应该调用 onIntervalChanged
。
寻求帮助。我怎样才能实施这种测试?
我最终得到了这个解决方案。创建一个 Activity 实现接口并跟踪所有交互。
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);
}
我对创建一个单独的 class 仅用于测试目的并不完全满意。我想同样可以用 Mockito 或 Robolectric 实现,但我不知道如何实现。
所以我仍然愿意接受任何想法或建议。如果一周内没有人给出更好的解决方案,我会接受我自己的答案。
当你不控制生命周期时,让工作成为间谍总是很有挑战性的。
我们通常做的是将所有不相关的功能提取到实用程序 classes 并在测试中模拟它们。它还有助于应用程序的设计(单一 class 责任规则)。
当然,这取决于您是否对这些数据进行处理。如果它只是数据 class,那么我将 Factory
用于创建此数据 classes 并再次在测试中模拟它。所有这些都需要适当的 DI(看 Dagger)。
您的方法没有任何问题,但它不会强迫您将您的应用程序视为相互交互的小部件。但同时它带来了更多的复杂性,稍后会得到回报