Niedawno zintegrowałem komponent nawigacyjny w projekcie, który jest oparty na modelu pojedynczego działania. Próbowałem dodać testy interfejsu użytkownika na podstawie samouczków w dokumentacji systemu Android. Jednak to nie zadziałało, gdy zrobiłem małą modyfikację tego samouczka. Mam następujący kod w moim fragmencie.
val bundle = Bundle().apply {
putString(UserTrackingConstants.VIEW, UserTrackingConstants.HOME)
}
btnSignUp.setOnClickListener {
findNavController().navigate(R.id.action_from_home_fragment_to_registration_fragment,bundle)
}
Tak więc za każdym razem, gdy zostanie kliknięty btnSignUp, wywoła następującą akcję nawigacji. Działa idealnie. Następnie dodałem następujący test.
@Test
fun testRegisterButtonClicked() {
val mockNavController = mock(NavController::class.java)
val bundle = bundleOf(
UserTrackingConstants.VIEW to UserTrackingConstants.HOME
)
val scenario = launchFragmentInContainer<HomeFragment>()
scenario.onFragment { fragment ->
Navigation.setViewNavController(fragment.requireView(), mockNavController)
}
onView(withId(R.id.btnSignUp))
.perform(click())
verify(mockNavController).navigate(R.id.action_from_home_fragment_to_registration_fragment, bundle)
}
I rzuca następujący wyjątek
E/TestRunner: ----- begin exception -----
Argument(s) are different! Wanted:
navController.navigate(
2131296310,
Bundle[{view=home}]
);
-> at HomeFragmentTest.testRegisterButtonClicked(HomeFragmentTest.kt:87)
Actual invocation has different arguments:
navController.navigate(
2131296310,
Bundle[{view=home}]
);
-> at HomeFragment$setupObservers$2.onClick(HomeFragment.kt:84)
at HomeFragmentTest.testRegisterButtonClicked(HomeFragmentTest.kt:87)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:392)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
----- end exception -----
Argument(s) are different! Wanted:
navController.navigate(
2131296310,
Bundle[{view=home}]
);
-> at HomeFragmentTest.testRegisterButtonClicked(HomeFragmentTest.kt:87)
Actual invocation has different arguments:
navController.navigate(
2131296310,
Bundle[{view=home}]
);
-> at HomeFragment$setupObservers$2.onClick(HomeFragment.kt:84)
at HomeFragmentTest.testRegisterButtonClicked(HomeFragmentTest.kt:87)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:392)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
Z logcata nie wyciągam niczego użytecznego. Wygląda na to, że argumenty są takie same i powinno minąć. Działa dobrze, jeśli nie zdam pakietu. Śledziłem dokumenty w następującym linku https://developer.android.com/guide/ nawigacja/testy nawigacji. Moje pytanie brzmi, czy ktoś doświadcza takich wyjątków i jak przetestować składnik nawigacji z pakietem?
2 odpowiedzi
Powodem, dla którego doświadczyłeś tak dziwnych wyników testów jest to, że klasa Bundle
nie przesłania metod equals()
i toString()
, więc instancje Bundle
są porównywane przez referencję (a referencje są oczywiście inny dla oczekiwanego pakietu i rzeczywistego) – a ponieważ Mockito opiera się na tych metodach, aby sprawdzić, czy obiekty są takie same, Twój przypadek testowy zakończy się niepowodzeniem.
Aby naprawić to zachowanie, możesz użyć niestandardowego dopasowania dla Mockito.verify()
:
public class BundleEquals implements ArgumentMatcher, ContainsExtraTypeInfo, Serializable {
@Nullable
private final Bundle expected;
public BundleEquals(@Nullable Bundle expected) {
this.expected = expected;
}
@Override
public boolean matches(Bundle actual) {
if (expected == null && actual == null) {
return true;
}
if (expected == null || actual == null) {
return false;
}
return areBundlesEqual(expected, actual);
}
private boolean areBundlesEqual(@NonNull Bundle expected, @NonNull Bundle actual) {
if (expected.size() != actual.size()) {
return false;
}
if (!expected.keySet().containsAll(actual.keySet())) {
return false;
}
for (String key : expected.keySet()) {
@Nullable Object expectedValue = expected.get(key);
@Nullable Object actualValue = actual.get(key);
if (expectedValue instanceof Bundle && actualValue instanceof Bundle) {
if (!areBundlesEqual((Bundle) expectedValue, (Bundle) actualValue)) {
return false;
}
} else if (!Objects.equals(expectedValue, actualValue)) {
return false;
}
}
return true;
}
public String toStringWithType() {
String clazz = expected != null ? expected.getClass().getSimpleName() : null;
return "(" + clazz + ") " + describe(expected);
}
private String describe(Object object) {
return ValuePrinter.print(object);
}
@Override
public boolean typeMatches(Object actual) {
return expected != null && actual != null && actual.getClass() == expected.getClass();
}
public String toString() {
return describe(expected);
}
}
Uwaga: implementacje ContainsExtraTypeInfo
i Serializable
nie są tutaj wymagane, ale zapewnią szczegółowy opis oczekiwanych i rzeczywistych argumentów w przypadku niepowodzenia testu z powodu niezgodności pakietów,
Aby zastosować dopasowanie, powinieneś użyć czegoś takiego:
verify(mockNavController).navigate(eq(R.id.action_from_home_fragment_to_registration_fragment), argThat(new BundleEquals(bundle));
Pamiętaj, że eq()
jest tutaj potrzebny, aby Mockito nie myliło różnych dopasowań: pozostawienie niezapakowanego identyfikatora akcji nie powiedzie się weryfikacją.
Jesteś tego pewien:
bundleOf(
UserTrackingConstants.VIEW to UserTrackingConstants.HOME
)
Jest taki sam jak:
Bundle().apply {
putString(UserTrackingConstants.VIEW, UserTrackingConstants.HOME)
}
Podobne pytania
Powiązane pytania
Nowe pytania
android
Android to mobilny system operacyjny Google, używany do programowania lub tworzenia urządzeń cyfrowych (smartfony, tablety, samochody, telewizory, Wear, Glass, IoT). W przypadku tematów związanych z Androidem użyj tagów specyficznych dla Androida, takich jak android-intent, android-activity, android-adapter itp. W przypadku pytań innych niż programowanie lub programowanie, ale związanych ze strukturą Androida, użyj tego linku: https: // android.stackexchange.com.