在我的Android应用程序中,我有以下导航图:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/card_list">
<fragment
android:id="@+id/card_list"
android:name="bbct.android.common.fragment.BaseballCardList"
android:label="@string/app_name"
tools:layout="@layout/card_list">
<action
android:id="@+id/action_details"
app:destination="@id/card_details" />
</fragment>
<fragment
android:id="@+id/card_details"
android:name="bbct.android.common.fragment.BaseballCardDetails"
android:label="@string/card_details_title"
tools:layout="@layout/card_details">
<argument
android:name="id"
app:argType="long" />
</fragment>
</navigation>
可以看到,card_details
接受一个id
参数。如果这是-1
,那么详细视图将打开,其中包含所有空白字段,并允许用户创建新卡。如果id
为正值,则使用它从数据库中获取一行并填充表单以进行编辑。
现在我正在编写一个测试,以验证在编辑卡片时,当用户单击保存按钮时,应用程序导航回card_list
:
public class NavigationTest {
@Test
public void clickOnSaveNavigatesFromDetailsBackToList() {
TestNavHostController navController = new TestNavHostController(ApplicationProvider.getApplicationContext());
FragmentScenario<BaseballCardDetails> detailsScenario = FragmentScenario.launchInContainer(
BaseballCardDetails.class,
null,
R.style.AppTheme
);
detailsScenario.onFragment(fragment -> {
navController.setGraph(R.navigation.nav_graph);
BaseballCardDetailsArgs args = new BaseballCardDetailsArgs.Builder(0).build();
navController.setCurrentDestination(R.id.card_details, args.toBundle());
Navigation.setViewNavController(fragment.requireView(), navController);
});
assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId())
.isEqualTo(R.id.card_details);
Espresso.onView(ViewMatchers.withId(R.id.save_button)).perform(ViewActions.click());
assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId())
.isEqualTo(R.id.card_list);
}
}
然而,当这个测试导航到card_details
时,表单字段没有被卡片的值填充。为了看看发生了什么,我在我的应用程序中添加了Log.d()
调用:
public class BaseballCardDetails extends Fragment {
// ...
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// ...
populateTextEdits();
}
private void populateTextEdits() {
Bundle args = getArguments();
Log.d(TAG, "args: " + args);
// ...
}
}
这个输出
05-31 21:35:13.589 29002 29002 D bbct.android.common.fragment.BaseballCardDetails: args: null
可以看到args
是null
。为什么测试中的参数没有传递给这里的片段?更重要的是,我该如何修复它?
当你写:
FragmentScenario<BaseballCardDetails> detailsScenario = FragmentScenario.launchInContainer(
BaseballCardDetails.class,
null,
R.style.AppTheme
);
那null
是传递给你的片段的参数。因此,getArguments()
是空的,因为你已经传递给你的片段的参数是空的。因为NavController没有在你的测试中创建你的片段(相反,它是launchInContainer
创建的),你通过setCurrentDestination
设置的参数只适用于NavBackEntry
的参数(即,如果你调用navController.getCurrentBackStackEntry().getArguments()
)。
不是将null
传递给launchInContainer
,而是将args
Bundle的构造移到调用launchInContainer
之前:
@Test
public void clickOnSaveNavigatesFromDetailsBackToList() {
TestNavHostController navController = new TestNavHostController(ApplicationProvider.getApplicationContext());
// Move args construction here
BaseballCardDetailsArgs args = new BaseballCardDetailsArgs.Builder(0).build();
Bundle bundleArgs = args.toBundle();
// And pass the bundleArgs into launchInContainer:
FragmentScenario<BaseballCardDetails> detailsScenario = FragmentScenario.launchInContainer(
BaseballCardDetails.class,
bundleArgs,
R.style.AppTheme
);
detailsScenario.onFragment(fragment -> {
navController.setGraph(R.navigation.nav_graph);
// Reuse the same bundleArgs here as well
navController.setCurrentDestination(R.id.card_details, bundleArgs);
Navigation.setViewNavController(fragment.requireView(), navController);
});
assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId())
.isEqualTo(R.id.card_details);
Espresso.onView(ViewMatchers.withId(R.id.save_button)).perform(ViewActions.click());
assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId())
.isEqualTo(R.id.card_list);
}