gpt4 book ai didi

安卓 : How to write a unit test for fragment depending on a viewmodel Live data attribute?

转载 作者:行者123 更新时间:2023-12-02 12:34:42 34 4
gpt4 key购买 nike

我的 fragment UI 中有一个 ListView ,它的元素集取决于来自 viewmodel LiveData 属性的值的状态。

我想为包含与该属性的值集相关的 3 个场景测试用例的 fragment 创建工具测试,但我不知道从哪里开始。

我的代码应该如下所示:

class MyViewModel : ViewModel() {
var status = MutableLiveData("")
}


class MyFragment : Fragment() {

private lateinit var myViewModel: MyViewModel

private lateinit var myListView: ListView

override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)

myViewModel =
ViewModelProviders.of(this, ViewModelProvider.Factory).get(MyViewModel::class.java)
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {

when (myViewModel?.status) {

"status1":
setListContent(items1)

"status1":
setListContent(items2)

"status1":
setListContent(items3)

else
setListContent
(items1)

}
}

private fun setListContent(itemsList: List<?>) {
myListView.adapter = MyCustomadapter(context!!, itemsList)
}

}

最佳答案

首先,您应该将 fragment 本身的编写测试与 View 模型和实时数据的测试分开。

由于您想根据 View 模型实时数据为 fragment 编写测试,那么我认为一个解决方案是模拟 View 模型(或 View 模型所依赖的存储库)并使用 FragmentScenario 启动您的 fragment 并对其进行测试。就像在这个 codelab 中所做的一样.

编辑:根据您提供的新代码

首先,我对您的代码进行了一些更改,以使其可运行和可测试(此代码只是运行的代码,仅用于测试,不是格式良好且编写良好的代码):

我的 fragment :

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ListView
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider.Factory
import androidx.lifecycle.ViewModelProviders

class MyFragment : Fragment() {

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
lateinit var myViewModel: MyViewModel

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
lateinit var myListView: ListView

override fun onAttach(context: Context) {
super.onAttach(context)

val FACTORY = object : Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel() as T
}
}
myViewModel =
ViewModelProviders.of(this, FACTORY).get(MyViewModel::class.java)
myListView = ListView(context)
myListView.adapter = MyCustomadapter(context, listOf("a", "b", "c"))

}

val items1 = listOf("a", "b", "c")
val items2 = listOf("1", "2")
val items3 = listOf("a1", "a2", "a3")

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
when (myViewModel.status.value) {

"status1" ->
setListContent(items1)

"status2" ->
setListContent(items2)

"status3" ->
setListContent(items3)

else -> setListContent(items1)
}
return View(context)
}

private fun setListContent(itemsList: List<String>) {
myListView.adapter = MyCustomadapter(context!!, itemsList)
}
}

我的自定义适配器:

import android.content.Context
import android.database.DataSetObserver
import android.view.View
import android.view.ViewGroup
import android.widget.ListAdapter

class MyCustomadapter(private val context: Context, private val itemsList: List<String>) : ListAdapter {
override fun isEmpty(): Boolean {
return itemsList.isEmpty()
}

override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
return View(context)
}

override fun registerDataSetObserver(p0: DataSetObserver?) {

}

override fun getItemViewType(p0: Int): Int {
return 1
}

override fun getItem(p0: Int): Any {
return itemsList[p0]
}

override fun getViewTypeCount(): Int {
return 3
}

override fun isEnabled(p0: Int): Boolean {
return true
}

override fun getItemId(p0: Int): Long {
return 0
}

override fun hasStableIds(): Boolean {
return true
}

override fun areAllItemsEnabled(): Boolean {
return true
}

override fun unregisterDataSetObserver(p0: DataSetObserver?) {

}

override fun getCount(): Int {
return itemsList.size
}

}

我的 View 模型:
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MyViewModel : ViewModel() {
var status = MutableLiveData<String>()
}

在上面的代码中,我使用了@VisibleForTesting 注解来测试私有(private)字段。 [但我建议不要这样做,而是使用公共(public)方法或 UI 组件来测试代码行为。由于您在这里没有提供任何 UI 组件,因此我没有其他简单的选择来测试您的代码]。

现在我们将依赖项添加到应用模块的 build.gradle:
testImplementation 'junit:junit:4.12'
debugImplementation 'androidx.fragment:fragment-testing:1.1.0'
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'org.robolectric:robolectric:4.3.1'
testImplementation 'androidx.arch.core:core-testing:2.1.0'

君特 : 用于纯单元测试 ['pure' 意味着您不能在 junit 测试中使用 android 相关代码]。我们总是需要这个库来编写我们的 android 测试。

fragment 测试 : 使用 FragmentScenario .对于 avoid robolectric style problem我们使用“debugImplementation”而不是“testImplementation”。

androidx.test.ext:junit : 用于使用 AndroidJUnit4 测试运行器。

机器人电动 :我们在这里使用 robolectric 在 JVM 上运行 android 仪器测试 - 在本地(而不是在 android 模拟器或物理设备上运行)。

androidx.arch.core:核心测试 :我们用它来测试实时数据

为了能够在 robolectric 中使用 android 资源,我们需要在 app build.gradle 中添加一个测试选项:
android {
...
testOptions {
unitTests {
includeAndroidResources = true
}
}
}

最后,我们编写一个简单的测试 :

[将此测试放在“测试”源集中,而不是放在“androidTest”中。您还可以通过在 android studio 中按 Ctrl + Shift + T 或右键单击类名并按 generate>Test... 并选择“test”源集来为您的代码创建测试文件]:
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.fragment.app.testing.launchFragment
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class MyFragmentTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()

@Test
fun changingViewModelValue_ShouldSetListViewItems() {
val scenario = launchFragment<MyFragment>()
scenario.onFragment { fragment ->
fragment.myViewModel.status.value = "status1"
assert(fragment.myListView.adapter.getItem(0) == "a")
}
}
}

在上面的测试中,我们通过设置实时数据值来测试设置 ListView 项。 'InstantTaskExecutorRule' 用于确保实时数据值将以可预测的方式进行测试(如 here 所述)。

Source set structure

如果您想使用 Espresso 或其他库等库测试您的 UI 组件(例如测试屏幕中显示的项目),请首先将其依赖项添加到 gradle,然后更改 launchFragment<MyFragment>()launchFragmentInContainer<MyFragment>()如所述 here .

关于安卓 : How to write a unit test for fragment depending on a viewmodel Live data attribute?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59666993/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com