Android:如何根据视图模型实时数据属性为片段编写单元测试?

2024-05-11

我的片段 UI 中有一个列表视图,其元素集取决于来自视图模型 LiveData 属性的值的状态。

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

我的代码应该如下所示:

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)
}

}


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

因为你想编写测试取决于视图模型的片段实时数据,那么我认为解决方案是模拟视图模型(或视图模型依赖的存储库)并使用 FragmentScenario 启动片段并测试它。就像在这个中所做的那样codelab https://codelabs.developers.google.com/codelabs/advanced-android-kotlin-training-testing-test-doubles/#0.

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

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

我的片段:

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
    }

}

我的视图模型:

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

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

在上面的代码中,我使用 @VisibleForTesting 注释来测试私有字段。 [但我建议不要这样做,而是使用公共方法或 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'

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

片段测试:用于使用片段场景 https://developer.android.com/training/basics/fragments/testing. For 避免机器人电动风格问题 https://stackoverflow.com/questions/54007609/robolectric-androidx-fragments-noclassdeffounderror我们使用“debugImplementation”而不是“testImplementation”。

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

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

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

为了能够在 robolectric 中使用 android 资源,我们需要向应用程序 build.gradle 添加一个测试选项:

android {
    ...
    testOptions {
        unitTests {
            includeAndroidResources = true
        }
    }
}

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

[将此测试放在“test”源集中而不是“androidTest”中。您还可以通过在 android studio 中按 Ctrl + Shift + T 或右键单击类名并按“生成”>“测试...”并选择“测试”源集来为代码创建测试文件]:

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")
        }
    }
}

在上面的测试中,我们通过设置实时数据值来测试设置列表视图项。 “InstantTaskExecutorRule”用于确保以可预测的方式测试实时数据值(如所解释的here https://codelabs.developers.google.com/codelabs/advanced-android-kotlin-training-testing-basics/index.html?index=..%2F..index#8).

如果您想使用 Espresso 或其他库等库测试您的 UI 组件(例如测试屏幕中显示的项目),请首先将其依赖项添加到 gradle,然后更改launchFragment<MyFragment>() to launchFragmentInContainer<MyFragment>()如上所述here https://developer.android.com/training/basics/fragments/testing#create.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android:如何根据视图模型实时数据属性为片段编写单元测试? 的相关文章

  • 如何在应用程序关闭时在 Android 通知中显示操作按钮?

    我有一个安卓应用程序 对于通知 我们必须显示一些操作按钮 当应用程序打开时 我们可以自由地构建通知并显示操作按钮 但是当应用程序关闭时 通知会在 Android 的通知托盘中收到 应用程序开发人员无法控制构建用户界面和操作按钮 我们现在如何
  • 在 Android 上使用 AT 命令与调制解调器对话

    我试图与三星 Galaxy s2 plus 和华为 p1 xl u9200 调制解调器发送 AT 命令 但无法得到任何结果 我使用 adb shell 发送命令并使用 logcat 查看日志 在三星 当我执行 cat 时 proc tty
  • 如何在Python中模拟依赖关系

    我是 python 单元测试框架的新手 并且在模拟依赖项方面存在很多困惑 我正在尝试为类的以下成员函数编写单元测试 check something class Validations def check something self abc
  • 拖动时跳转 ImageView。 getX() 和 getY() 值正在跳跃

    我创建了一个用于拖动视图的 onTouchListener 如果我使用的话 图像可以顺利拖动getRawX and getRawY 问题是 当您向下放置第二个指针然后抬起第一个指针时 图像将跳转到第二个指针 此 onTouchListene
  • 带有 Android 支持库 v7 的 Maven Android 插件

    我使用 maven android plugin 构建我的 android 应用程序 它依赖于 android 支持库 v4 和 v7 由于我没有找到如何从developer android com下载整个sdk 因此我无法使用maven
  • Android 中的列表(特别是 RecyclerView 和 CardView)如何工作

    请原谅我问这个问题 但我是 Android 开发新手 尽管我正在尝试了解developer android com 网站上的基础知识 但大多数示例 即使他们说它们是为 Android Studio 构建的 尚未设置为使用 Gradle 因此
  • 如果使用grifika的ContinualCaptureActivity中的预览方式,相机预览的视野会更小

    我们知道 当相机预览比例设置为时 在相同距离下我们会得到更大的预览视野4 3代替16 9 具体如下 Android Camera API 奇怪的缩放效果 https stackoverflow com questions 20664628
  • 从ListView中隐藏行而不占用空间

    我有一个带有关联 ArrayAdapter 的 ListView 它在多个活动中显示其内容 不幸的是 现在有必要 我的 ListView 在其中一项设置中不显示其所有元素 而仅显示 属性 未设置为 true 的元素 我想避免使用两个具有不同
  • 将 java 中的 byte[] 转换为 C++ 中的 unsigned char* 的正确方法,反之亦然?

    我是 C 和 JNI 的新手 我尝试找到一种正确的方法 通过使用 JNI 将 java 中的 byte 转换为 C 中的 unsigned char 反之亦然 我正在安卓上工作 在谷歌和SO中寻找解决方案后 我还没有找到将java中的byt
  • 选项卡主机内的 Android Fragment 视图状态 [重复]

    这个问题在这里已经有答案了 可能的重复 使用 Fragment 为 Android 中的每个选项卡单独的返回堆栈 https stackoverflow com questions 6987334 separate back stack f
  • Android蓝牙java.io.IOException:bt套接字已关闭,读取返回:-1

    我正在尝试编写一个代码 仅连接到运行 Android 5 0 KitKat 的设备上的 目前 唯一配对的设备 无论我尝试了多少方法 我仍然会收到此错误 这是我尝试过的最后一个代码 它似乎完成了我看到人们报告为成功的所有事情 有人能指出我做错
  • MIUI 权限被拒绝活动 KeyguardLocked

    当应用程序处于后台且屏幕被锁定时 我无法启动活动 没有异常或警告 只是不调用 onCreate 我一直在与这个问题作斗争 我想我终于找到了它的根源 日志中有一行 D com android server am ExtraActivityMa
  • 当 minifyEnabled 为 true 时 Android 应用程序崩溃

    我正在使用多模块应用程序 并且该应用程序崩溃时minifyEnabled true in the installed模块的build gradle 以下是从游戏控制台检索到的反混淆堆栈跟踪 FATAL EXCEPTION Controlle
  • 运行 Python 单元测试,以便成功时不打印任何内容,失败时仅打印 AssertionError()

    我有一个标准单元测试格式的测试模块 class my test unittest TestCase def test 1 self tests def test 2 self tests etc 我的公司有一个专有的测试工具 它将作为命令行
  • 如何通过 Android 按钮单击运行单独的应用程序

    我尝试在 Android 应用程序中添加两个按钮 以从单独的两个应用程序订单系统和库存系统中选择一个应用程序 如图所示 我已将这两个应用程序实现为两个单独的 Android 项目 当我尝试运行此应用程序时 它会出现直到正确选择窗口 但是当按
  • Unity c# 四元数:将 y 轴与 z 轴交换

    我需要旋转一个对象以相对于现实世界进行精确旋转 因此调用Input gyro attitude返回表示设备位置的四元数 另一方面 这迫使我根据这个四元数作为默认旋转来计算每个旋转 将某些对象设置为朝上的简单方法如下 Vector3 up I
  • 离子初始加载时间

    我正在使用 Ionic 构建一个简单的应用程序 但我的应用程序在冷启动时的初始加载时间方面存在性能问题 这是我所做的 collection repeat 代替带有 track by 的 ng repeat 原生滚动 overflow scr
  • Android 标记如何实现拖放?

    你好 我正在 Android 中开发 MapView 应用程序 我有三个标记 我希望稍后能够使用 Google Map API getlocation function 为了尝试一下 我想使用拖放功能移动标记 然后检查位置 任何人都可以通过
  • 将对象从手机共享到 Android Wear

    我创建了一个应用程序 在此应用程序中 您拥有包含 2 个字符串 姓名和年龄 和一个位图 头像 的对象 所有内容都保存到 sqlite 数据库中 现在我希望可以在我的智能手表上访问这些对象 所以我想实现的是你可以去启动 启动应用程序并向左和向
  • Android GetPTLAFormat 上的 Phonegap 错误

    我们正在开发一个使用 jQuery 移动和电话间隙的应用程序 一切似乎都工作正常 但是当在连接的 Android 手机上运行应用程序时 我们在 Eclipse logcat 中看到大量类似这样的错误 0 GetPTLAFormat inva

随机推荐

  • 无法弄清楚为什么 Bison 抛出“由于冲突,规则在解析器中无用”

    我正在为一种非常简单的编程语言编写 BNF 语法 并使用 Flex 和 Bison 进行编译 我只有 3 种变量和常量类型 实数 整数 字符串 我的 l 文件具有 ID 的标记定义 如下所示 DIGIT 0 9 LETTER a zA Z
  • 有没有办法将 Excel 单元格条目转换为一致的日期和时间格式?

    我正在处理雨量计数据记录器生成的 csv 文件中的一些雨量计数据 我发现日期和时间的记录不一致 以以下两种格式之一交替显示 Format 1 mm dd yyyy hh mm 24 hour clock or Format 2 mm dd
  • WordPress 访问

    我正在与朋友一起开发一个网站 使用Wordpress我们正在尝试从我的计算机和他的计算机访问同一个 WordPress 帐户 以便我们可以一起在网站上工作 我们尝试将彼此添加为管理员 但只能从创建管理员的计算机上访问新帐户 有谁知道如何做到
  • Android布局中如何实现Button的自动宽度

    我有几个垂直列出的按钮 我需要它们都具有相同的宽度 而且还要显示其中的所有文本 基本上我需要所有它们的宽度作为最大的宽度的包裹宽度 希望我解释得很好 现在 我已经有一种布局可以在我的 Samsung Galaxy S2 4 1 2 上运行
  • 基于 json 文件动态显示选择、复选框、日期选择器等

    对我之前的问题的补充 我根据 json 文件动态显示输入字段 现在我想根据它们的组显示选择项 复选框和日期选择器 我如何解决这个问题 我需要将这些元素推入computeJSON 但写入例如选择options item selection不管
  • 检测 SVGAnimatedString 的类名

    我在构建 SVG 地图时遇到问题 触发的功能 g 上的 onmouseover 不起作用 我当时用的 window onmouseover function e console log e target className 查看类名是否有问
  • 如何在 Java 中用 \n 替换 \\n

    我有一个string test first n middle n last 现在我想更换所有 n by n 我试过了test replaceAll n n and test replaceAll n n 但它们不起作用 有人有解决办法吗 T
  • 如果使用 unicode 字符,则从数据库中进行 SELECT 会出现问题

    我在用着latest带DatabaseLibrary的python和机器人框架版本 https franz see github io Robotframework Database Library api 1 0 1 DatabaseLi
  • 正则表达式没有按预期工作?

    我有这个正则表达式 new RegExp a z 0 9 ig 我正在测试一个不应该工作的字符串 vc 但它确实通过了测试 而且它不应该 new RegExp a z 0 9 ig test vc true 但如果我删除其中一个 or or
  • static_cast 和对指针的引用

    谁能告诉我为什么这不能编译 struct A struct B public A int main B b A a b B b1 static cast
  • 如何从 Ruby 2.0 中的字符串数组获取值

    我有这个字符串数组 array nike air nike steam nike softy nike strength smooth sleeper adidas air addidas jogar adidas softy adidas
  • 我无法从 Android 模拟器中删除日语 IME

    我已经多次看到这个问题 但答案总是 从 语言和键盘设置 菜单中取消选中 IME 问题是那里没有复选框 选择菜单 自定义区域设置 en US 菜单 设置 语言和键盘 选择语言 选择 英语 美国 菜单 设置 语言和键盘 日语输入法是唯一的输入法
  • opencv形态扩张滤波器作为最大滤波器

    就像中值滤波器的定义一样 我可以将 最大滤波器 定义为局部窗口 例如dst x y max 3x3 局部窗口像素 但我在opencv中找不到这样的过滤器 最接近的是 dilate 函数 然后我使用 dilate 函数的默认配置 但结果不正确
  • crashlyticsGenerateSymbolsRelease - 超出 GC 开销限制

    大约一周前 我们已成功将 crashlytics 集成到我们的 cocos2d x C 项目 ndk 14b 中 一切看起来都很好 但大约几天前任务 crashlyticsUploadSymbolsRelease 开始抛出此错误 Execu
  • 如何将js文件从同一文件夹导入chrome扩展中的background.js

    我在导入与 background js 脚本库 位于同一库中的文件 score js 时遇到问题 我对 js 和 chrome 扩展都很陌生 我研究了 require js 并做了这个 背景 html h1 Tab Manager h1
  • 显示葡萄牙语字符 Android

    我正在开发一个 Android 应用程序 当它应该写 n o 或 cabe a 等单词 例如带有 或 的字符 时 它会写 或 我已经检查过源文件编码 它是UTF 8 看起来 UTF8 被解释为 ISO 8859 x 理论上 你可以提供 ja
  • 如何制作复选按钮? (带有标签的隐藏复选框作为按钮:仅限 CSS)

    Using 方法1 创建可点击标签 https stackoverflow com a 6293626 1326147 用 CSS 隐藏复选框 https stackoverflow com a 18078908 1326147 and 使
  • 如何从字符串转换为数组?

    If s 1 2 3 4 5 我们如何从中获得一个整数数组 我想返回 5 个元素Array Int64 1 1 2 3 4 5 正如 isebarn 使用的那样 split s 对于将字符串拆分为单词非常有用 默认情况下按空格拆分 juli
  • 如何隐藏 URL 中的 ID

    我以前在 Stack Overflow 上见过这类问题 但没有一个真正有帮助 我也用谷歌搜索过 但没有骰子 我想知道如果用户单击选项卡本身是否可以隐藏 URL 中的 ID 这是网页 www planet nu dev new experia
  • Android:如何根据视图模型实时数据属性为片段编写单元测试?

    我的片段 UI 中有一个列表视图 其元素集取决于来自视图模型 LiveData 属性的值的状态 我想为片段创建工具测试 该片段包含与该属性的值集相关的 3 个场景测试用例 但我不知道从哪里开始 我的代码应该如下所示 class MyView