从 ActionBar 菜单测试导航
Testing Navigation from ActionBar menu
我正在尝试为菜单项编写测试。这个想法是单击菜单项并验证 NavController 是否设置为正确的目的地:
public class NavigationTest {
@Test
public void clickOnSearchMenuNavigatesToFilter() {
TestNavHostController navController = new TestNavHostController(ApplicationProvider.getApplicationContext());
ActivityScenario<MainActivity> listScenario = ActivityScenario.launch(MainActivity.class);
listScenario.onActivity(activity -> {
navController.setGraph(R.navigation.nav_graph);
Navigation.setViewNavController(activity.requireViewById(R.id.nav_host_fragment), navController);
});
Espresso.onView(ViewMatchers.withId(R.id.filter_menu)).perform(ViewActions.click());
assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId()).isEqualTo(R.id.filter_cards);
}
}
此测试失败
expected: 2131296468
but was : 2131296378
2131296378
是 NavController
中初始片段的 ID。我的测试有什么问题?我最初的想法是我没有正确地注入 TestNavHostController
,但是调试验证了 activity 和我预期的一样:
public class MainActivity extends AppCompatActivity {
// ...
@Override
public boolean onOptionsItemSelected(MenuItem item) {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
return NavigationUI.onNavDestinationSelected(item, navController)
|| super.onOptionsItemSelected(item);
}
}
当我在这里设置断点时,我确认navController
是TestNavHostController
的一个实例。
为了完整起见,这是我的 nav_graph.xml
:
<?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" />
<action
android:id="@+id/action_filter"
app:destination="@id/filter_cards" />
<argument
android:name="filterParams"
app:argType="android.os.Bundle"
app:nullable="true" />
</fragment>
<fragment
android:id="@+id/about"
android:name="bbct.android.common.fragment.About"
android:label="@string/about_title"
tools:layout="@layout/about" />
<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>
<fragment
android:id="@+id/filter_cards"
android:name="bbct.android.common.fragment.FilterCards"
android:label="@string/filter_cards_title"
tools:layout="@layout/filter_cards">
<action
android:id="@+id/action_list"
app:destination="@id/card_list" />
</fragment>
<action
android:id="@+id/action_about"
app:destination="@id/about" />
</navigation>
有什么想法我遗漏了什么或我可以尝试解决此测试的任何问题吗?
附录:
请注意,我这里的测试使用 ActivityScenario
而不是 example from the documentation. I realize this might be an XY problem 中的 FragmentScenario
。我在此测试中使用了 ActivityScenario
,因为我需要单击 Activity
中托管的工具栏中的 MenuItem
。在我之前使用 FragmentScenario
的尝试中,未呈现工具栏。我愿意接受使用不同方法来测试我要编写的测试的建议。
为了将导航目的地绑定到菜单项,两者的 ID 需要匹配 as per documentation:
If the id of the MenuItem matches the id of the destination, the NavController can then navigate to that destination.
这不仅适用于 ActionBar
菜单,还适用于其他类型的菜单,例如导航抽屉和 BottomNavigationView
菜单。
没有它,导航将不会发生,并且测试将失败,因为当前目的地保持不变。
要解决此问题,R.id.filter_menu
和 R.id.filter_cards
都需要在菜单和 navGraph
XML 文件中匹配,并且在测试中也需要匹配,假设两者都是 filter_cards
,那么 R.id.filter_menu
需要在菜单文件和测试中更改为 R.id.filter_cards
:
Espresso.onView(ViewMatchers.withId(R.id.filter_cards)).perform(ViewActions.click());
我正在尝试为菜单项编写测试。这个想法是单击菜单项并验证 NavController 是否设置为正确的目的地:
public class NavigationTest {
@Test
public void clickOnSearchMenuNavigatesToFilter() {
TestNavHostController navController = new TestNavHostController(ApplicationProvider.getApplicationContext());
ActivityScenario<MainActivity> listScenario = ActivityScenario.launch(MainActivity.class);
listScenario.onActivity(activity -> {
navController.setGraph(R.navigation.nav_graph);
Navigation.setViewNavController(activity.requireViewById(R.id.nav_host_fragment), navController);
});
Espresso.onView(ViewMatchers.withId(R.id.filter_menu)).perform(ViewActions.click());
assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId()).isEqualTo(R.id.filter_cards);
}
}
此测试失败
expected: 2131296468
but was : 2131296378
2131296378
是 NavController
中初始片段的 ID。我的测试有什么问题?我最初的想法是我没有正确地注入 TestNavHostController
,但是调试验证了 activity 和我预期的一样:
public class MainActivity extends AppCompatActivity {
// ...
@Override
public boolean onOptionsItemSelected(MenuItem item) {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
return NavigationUI.onNavDestinationSelected(item, navController)
|| super.onOptionsItemSelected(item);
}
}
当我在这里设置断点时,我确认navController
是TestNavHostController
的一个实例。
为了完整起见,这是我的 nav_graph.xml
:
<?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" />
<action
android:id="@+id/action_filter"
app:destination="@id/filter_cards" />
<argument
android:name="filterParams"
app:argType="android.os.Bundle"
app:nullable="true" />
</fragment>
<fragment
android:id="@+id/about"
android:name="bbct.android.common.fragment.About"
android:label="@string/about_title"
tools:layout="@layout/about" />
<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>
<fragment
android:id="@+id/filter_cards"
android:name="bbct.android.common.fragment.FilterCards"
android:label="@string/filter_cards_title"
tools:layout="@layout/filter_cards">
<action
android:id="@+id/action_list"
app:destination="@id/card_list" />
</fragment>
<action
android:id="@+id/action_about"
app:destination="@id/about" />
</navigation>
有什么想法我遗漏了什么或我可以尝试解决此测试的任何问题吗?
附录:
请注意,我这里的测试使用 ActivityScenario
而不是 example from the documentation. I realize this might be an XY problem 中的 FragmentScenario
。我在此测试中使用了 ActivityScenario
,因为我需要单击 Activity
中托管的工具栏中的 MenuItem
。在我之前使用 FragmentScenario
的尝试中,未呈现工具栏。我愿意接受使用不同方法来测试我要编写的测试的建议。
为了将导航目的地绑定到菜单项,两者的 ID 需要匹配 as per documentation:
If the id of the MenuItem matches the id of the destination, the NavController can then navigate to that destination.
这不仅适用于 ActionBar
菜单,还适用于其他类型的菜单,例如导航抽屉和 BottomNavigationView
菜单。
没有它,导航将不会发生,并且测试将失败,因为当前目的地保持不变。
要解决此问题,R.id.filter_menu
和 R.id.filter_cards
都需要在菜单和 navGraph
XML 文件中匹配,并且在测试中也需要匹配,假设两者都是 filter_cards
,那么 R.id.filter_menu
需要在菜单文件和测试中更改为 R.id.filter_cards
:
Espresso.onView(ViewMatchers.withId(R.id.filter_cards)).perform(ViewActions.click());