我正在为我的应用程序使用androidx.fragment:fragment-testing
. 测试用例之一是检查所有底层逻辑在 Fragment 停止和恢复时是否正确运行,以模拟应用程序被最小化(主页按钮)并再次返回。这些测试利用FragmentScenario.moveToState()
. 首先,我使用 编写了我的测试androidx.fragment:fragment-testing:1.2.5
,它们都通过了。但是当我更新androidx.fragment:fragment-testing
到时1.3.1
,上述测试开始失败。
我检查了哪里出了问题,结果发现Fragment.onCreateView()
在生命周期更改期间再次调用它,即使它不应该(在返回CREATED
和返回的情况下RESUMED
),导致视图“重置”到布局中声明的初始状态。我查了一下,发现了一个错误,描述中提到“onCreateView() 生命周期方法被调用了两次” https://issuetracker.google.com/issues/143915710(它也在https://medium.com/androiddevelopers中提到/fragments-rebuilding-the-internals-61913f8bf48e)。问题是它已经在 Fragment 1.3.0-alpha08 中修复,所以它不应该在 1.3.1 中发生。这意味着我的项目配置一定有问题。
这是重现该问题的示例代码。它表明视图不会保留其文本,也不会保留生命周期更改 RESUMED -> CREATED -> RESUMED 的可见性。手动测试不会重现此问题,它只会影响仪器测试。
class LifecycleBugFragment : Fragment() {
lateinit var textView: TextView
lateinit var editText: EditText
lateinit var button: Button
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_lifecycle_bug, container, false)
textView = view.findViewById<TextView>(R.id.textView)
textView.setOnClickListener { textView.text = "I was clicked" }
editText = view.findViewById<EditText>(R.id.editText)
button = view.findViewById<Button>(R.id.button)
button.setOnClickListener { button.visibility = View.GONE }
return view
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".fragmenttesting.LifecycleBugFragment">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="default text" />
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="click to hide me"
/>
</LinearLayout>
const val TYPED_TEXT = "some example text"
const val DEFAULT_TEXT = "default text"
const val CLICKED_TEXT = "I was clicked"
class LifecycleBugFragmentTest {
lateinit var fragmentScenario: FragmentScenario<LifecycleBugFragment>
@Before
fun setUp() {
fragmentScenario = FragmentScenario.launchInContainer(LifecycleBugFragment::class.java)
}
@Test
fun whenTextViewclickedAndFragmentLifecycleStoppedAndResumed_ThenTextViewTextIsStillChanged() {
onView(withId(R.id.textView)).check(matches(withText(DEFAULT_TEXT)))
onView(withId(R.id.textView)).perform(click())
onView(withId(R.id.textView)).check(matches(withText(CLICKED_TEXT)))
stopAndResumeFragment()
onView(withId(R.id.textView)).check(matches(withText(CLICKED_TEXT)))
}
// this test passes, others fail
@Test
fun whenEditTextIsEditedAndFragmentLifecycleStoppedAndResumed_ThenEditTextTextIsStillChanged() {
onView(withId(R.id.editText)).perform(typeText(TYPED_TEXT))
stopAndResumeFragment()
onView(withId(R.id.editText)).check(matches(withText(TYPED_TEXT)))
}
@Test
fun whenButtonIsClickedAndFragmentLifecycleStoppedAndResumed_ThenButtonISStillNotVisible() {
onView(withId(R.id.button)).perform(click())
onView(withId(R.id.button)).check(matches(not(isDisplayed())))
stopAndResumeFragment()
onView(withId(R.id.button)).check(matches(not(isDisplayed())))
}
private fun stopAndResumeFragment() {
fragmentScenario.moveToState(Lifecycle.State.CREATED)
fragmentScenario.moveToState(Lifecycle.State.RESUMED)
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.31"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "androidx.test:runner:1.3.0"
androidTestImplementation "androidx.test:core:1.3.0"
androidTestImplementation "androidx.test.ext:junit:1.1.2"
androidTestImplementation "androidx.test:rules:1.3.0"
implementation "androidx.navigation:navigation-fragment-ktx:2.3.4"
implementation "androidx.navigation:navigation-ui-ktx:2.3.4"
androidTestImplementation "androidx.navigation:navigation-testing:2.3.4"
debugImplementation "androidx.fragment:fragment-testing:1.3.1"
implementation "androidx.navigation:navigation-compose:1.0.0-alpha09"
// other dependencies unrelated to issue skipped for clarity
}
由于我没有androidx.fragment:fragment
直接声明,它是一个传递依赖,所以我想知道它是否可能被解析为小于 8 的 1.3.0-alpha,因此不包含修复。我添加了依赖约束以确保解决 1.3.1
constraints {
implementation('androidx.fragment:fragment:1.3.1') {
because 'avoid bug'
}
implementation('androidx.fragment:fragment-ktx:1.3.1') {
because 'avoid bug'
}
}
但它没有帮助,所以情况并非如此
我的代码还有什么问题(很可能是 gradle 依赖项)?