在 Android 上测试 Kotlin 数据流
文章目录
- 一 创建虚构数据提供方
- 二 在测试中断言数据流发出
- 测试期间持续收集
- 三 测试 StateFlow
- 使用 stateIn 创建的 StateFlow
转自:
https://developer.android.google.cn/kotlin/flow/test?hl=zh-cn#producer
与数据流进行通信的单元或模块的测试方式取决于受测对象使用数据流作为输入还是输出
- 如果受测对象观察到数据流,您可以在虚构依赖项中生成数据流,而这些可以通过测试进行控制
- 如果单元或模块公开了数据流,您可以读取并验证测试中的数据流所发出的一个或多个数据项
一 创建虚构数据提供方
当受测对象是数据流使用方时,一种常见的测试方法是用虚构实现替换提供方。
class MyFakeRepository : MyRepository {fun observeCount() = flow {emit(ITEM_1)}
}
@Test
fun myTest() {// Given a class with fake dependencies:val sut = MyUnitUnderTest(MyFakeRepository())// Trigger and verify...
}
二 在测试中断言数据流发出
1、某些测试,您只需要检查来自数据流的第一个发出项或有限数量的项
@Test
fun myRepositoryTest() = runTest {// Given a repository that combines values from two data sources:val repository = MyRepository(fakeSource1, fakeSource2)// When the repository emits a valueval firstItem = repository.counter.first() // Returns the first item in the flow// Then check it's the expected itemassertEquals(ITEM_1, firstItem)
}
2、如果该测试需要检查多个值,则调用 toList() 会使数据流等待数据源发出其所有值,然后以列表形式返回这些值
@Test
fun myRepositoryTest() = runTest {// Given a repository with a fake data source that emits ALL_MESSAGESval messages = repository.observeChatMessages().toList()// When all messages are emitted then they should be ALL_MESSAGESassertEquals(ALL_MESSAGES, messages)
}
3、对于需要更复杂地收集数据项或未返回有限数据项的数据流,您可使用 Flow API 选取并转换数据项。
// Take the second item
outputFlow.drop(1).first()// Take the first 5 items
outputFlow.take(5).toList()// Takes the first item verifying that the flow is closed after that
outputFlow.single()// Finite data streams
// Verify that the flow emits exactly N elements (optional predicate)
outputFlow.count()
outputFlow.count(predicate)
测试期间持续收集
在测试中使用虚构实现时,您可以创建一个收集协程,该协程会持续接收 Repository 中的值
class Repository(private val dataSource: DataSource) {fun scores(): Flow<Int> {return dataSource.counts().map { it * 10 }}
}class FakeDataSource : DataSource {private val flow = MutableSharedFlow<Int>()suspend fun emit(value: Int) = flow.emit(value)override fun counts(): Flow<Int> = flow
}
可以创建一个收集协程,该协程会持续接收 Repository 中的值。
@Test
fun continuouslyCollect() = runTest {val dataSource = FakeDataSource()val repository = Repository(dataSource)val values = mutableListOf<Int>()backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {repository.scores().toList(values)}dataSource.emit(1)assertEquals(10, values[0]) // Assert on the list contentsdataSource.emit(2)dataSource.emit(3)assertEquals(30, values[2])assertEquals(3, values.size) // Assert the number of items collected
}
由于此处 Repository 公开的数据流永远无法完成,因此收集它的 toList 调用永远不会返回。
使用 Turbine 库
第三方 Turbine 库提供了一个用于创建收集协程的便捷 API,以及用于测试数据流的其他便捷功能
@Test
fun usingTurbine() = runTest {val dataSource = FakeDataSource()val repository = Repository(dataSource)repository.scores().test {// Make calls that will trigger value changes only within test{}dataSource.emit(1)assertEquals(10, awaitItem())dataSource.emit(2)awaitItem() // Ignore items if needed, can also use skip(n)dataSource.emit(3)assertEquals(30, awaitItem())}
}
三 测试 StateFlow
StateFlow 是一种可观察的数据存储器,可以收集这种存储器来以数据流的形式观察它随时间变化的存储值
以下 ViewModel 会从 Repository 收集值,并在 StateFlow 中将值提供给界面
class MyViewModel(private val myRepository: MyRepository) : ViewModel() {private val _score = MutableStateFlow(0)val score: StateFlow<Int> = _score.asStateFlow()fun initialize() {viewModelScope.launch {myRepository.scores().collect { score ->_score.value = score}}}
}
此 Repository 的虚构实现可能如下:
class FakeRepository : MyRepository {private val flow = MutableSharedFlow<Int>()suspend fun emit(value: Int) = flow.emit(value)override fun scores(): Flow<Int> = flow
}
使用此虚构实现测试 ViewModel 时,您可以从虚构实现发出值,以在 ViewModel 的 StateFlow 中触发更新,然后对更新后的 value 断言:
@Test
fun testHotFakeRepository() = runTest {val fakeRepository = FakeRepository()val viewModel = MyViewModel(fakeRepository)assertEquals(0, viewModel.score.value) // Assert on the initial value// Start collecting values from the RepositoryviewModel.initialize()// Then we can send in values one by one, which the ViewModel will collectfakeRepository.emit(1)assertEquals(1, viewModel.score.value)fakeRepository.emit(2)fakeRepository.emit(3)assertEquals(3, viewModel.score.value) // Assert on the latest value
}
使用 stateIn 创建的 StateFlow
ViewModel 使用 MutableStateFlow 存储 Repository 中的数据流发出的最新值。这是一种常见的模式,通常通过使用 stateIn 运算符以更简单的方式实现,该运算符会将冷数据流转换为热 StateFlow:
class MyViewModelWithStateIn(myRepository: MyRepository) : ViewModel() {val score: StateFlow<Int> = myRepository.scores().stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000L), 0)
}
相关文章:
在 Android 上测试 Kotlin 数据流
文章目录 一 创建虚构数据提供方二 在测试中断言数据流发出测试期间持续收集 三 测试 StateFlow使用 stateIn 创建的 StateFlow 转自: https://developer.android.google.cn/kotlin/flow/test?hlzh-cn#producer 与数据流进行通信的单元或模块的测试方式取决于受测对…...
day43
今日内容 python操作MySQL(重要) SQL注入问题(安全相关的xss,csrf) 视图(了解) 触发器(了解) 事务(重要) 存储过程(了解) 内置函数(了解,很多) 流程控制(了解) 索引(重点) python操作MySQL MySQL本身就是一款c/s架构,有服务端、有客户端&…...
终端管理制度
1、总则 1.1、目的 为规范XXXXX单位员工在使用计算机终端过程中的行为,提高计算机终端的安全性,确保员工安全使用计算机终端,特制定本制度。 1.2、范围 本规定适用于在XXXXX单位使用计算机终端的所有员工,包括内部终端和外部终…...
视频相关学习笔记
YUV 和rgb一样是一种表示色彩的格式,Y表示亮度,UV表示色度(U是蓝色投影,V是红色投影),只有Y就是黑白的,所以这个格式的视频图片可以兼容黑白电视,所以彩色电视使用的都是YUV 存储方…...
神经网络中epoch、batch、batchsize区别
目录 1 epoch 2 batch 3 batchsize 4 区别 1 epoch 当数据集中的全部数据样本通过神经网络一次并且返回一次的过程即完成一次训练称为一个epoch。 当我们分批学习时,每次使用过全部训练数据完成一次Forword运算以及一次BP运算,称为完成了一次epoch。 epoch时期 = 所有训练…...
如何将Mysql数据库的表导出并导入到另外的架构
如何将Mysql数据库的表导出并导入到另外的架构 准备一、解决方法1.右键->导出->用mysqldump导出2.注意路径一般为:C:/Program Files/MySQL/MySQL Server 8.0/bin/mysqldump.exe和导出的sql文件位置3.右键->SQL脚本->运行SQL脚本4.找到SQL脚本并点击确定…...
【tio-websocket】9、服务配置与维护—TioConfig
场景 我们在写 TCP Server 时,都会先选好一个端口以监听客户端连接,再创建N组线程池来执行相关的任务,譬如发送消息、解码数据包、处理数据包等任务,还要维护客户端连接的各种数据,为了和业务互动,还要把这些客户端连接和各种业务数据绑定起来,譬如把某个客户端绑定到一…...
数据结构—线性表(下)
文章目录 6.线性表(下)(4).栈与队列的定义和ADT#1.ADT#2.栈的基本实现#3.队列的形式#4.队列的几种实现 (5).栈与队列的应用#1.栈的应用i.后缀表达式求值ii.中缀表达式转后缀表达式 #2.队列的应用 (6).线性表的其他存储方式#1.索引存储#2.哈希存储i.什么是哈希存储ii.碰撞了怎么…...
apisix之插件开发,包含java和lua两种方式
https://download.csdn.net/download/tiantangpw/88475630 有ppt和springboot程序包,可以运行...
【面试经典150 | 链表】合并两个有序链表
文章目录 Tag题目来源题目解读解题思路方法一:递归方法二:迭代 写在最后 Tag 【递归】【迭代】【链表】 题目来源 21. 合并两个有序链表 题目解读 合并两个有序链表。 解题思路 一种朴素的想法是将两个链表中的值存入到数组中,然后对数组…...
【linux】麒麟v10安装Redis主从集群(ARM架构)
安装redis单示例的请看:麒麟v10安装Redis(ARM架构) 安装环境 HostnameIP addressmaster192.168.0.1slave1192.168.0.2slave2192.168.0.3 下载安装包 (三台都操作) wget https://repo.huaweicloud.com/kunpeng/…...
解决k8s删除名称空间无法强制删除的问题
问题起因:删除k8s名称空间的时候(此时名称空间下还有很多pod)一直删不掉,被我强行ctrl c了, 问题表象:然后就出现下面这悲催的一幕了,两个名称空间一直处于Terminating了 [rootmaster02 ~]# ku…...
华为---DHCP中继代理简介及示例配置
DHCP中继代理简介 IP动态获取过程中,客户端(DHCP Client)总是以广播(广播帧及广播IP报文)方式来发送DHCPDISCOVER和DHCPREQUEST消息的。如果服务器(DHCP Server)和 客户端不在同一个二层网络(二…...
五、W5100S/W5500+RP2040树莓派Pico<UDP Client数据回环测试>
文章目录 1. 前言2. 协议简介2.1 简述2.2 优点2.3 应用 3. WIZnet以太网芯片4. UDP Client回环测试4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 测试现象 5. 注意事项6. 相关链接 1. 前言 UDP是一种无连接的网络协议,它提供了一种简单的、不可靠的方式来…...
死锁Deadlock
定义 死锁是指两个或多个线程互相持有对方所需的资源,从而导致它们无法继续执行的情况。如下图所示,现有两个线程,分别是线程A及线程B,线程A持有锁A,线程B持有锁B。此时线程A想获取锁B,但锁B需等到线程B的结…...
【spark客户端】Spark SQL CLI详解:怎么执行sql文件、注释怎么写,支持的文件路径协议、交互式模式使用细节
文章目录 一. Spark SQL Command Line Options(命令行参数)二. The hiverc File1. without the -i2. .hiverc 介绍 三. 支持的路径协议四. 支持的注释类型五. Spark SQL CLI交互式命令六. Examples1. running a query from the command line2. setting Hive configuration vari…...
虹科干货 | HK-TrueNAS版本大揭秘!一文教您如何选择合适的TrueNAS软件
文章来源:虹科网络基础设施 阅读原文:https://mp.weixin.qq.com/s/Iv0zDDmiDgE9vEGlAZs-sg 1.导语 TrueNAS是虹科iXsystems 设计和开发的NAS 操作系统,提供许多功能,例如文件存储、虚拟机 (VM) 和媒体服务器。它基于…...
前端html+css+js实现的2048小游戏,很完善。
源码下载地址 支持:远程部署/安装/调试、讲解、二次开发/修改/定制 逻辑用的是JavaScript,界面用canvas实现,暂时还没有添加动画。 视频浏览地址...
学习通签到
要在Vue中使用H5lock.js,首先需要将H5lock.js引入到项目中。可以通过以下步骤来使用: 1. 将H5lock.js文件保存到项目中的某个目录下,例如src/assets文件夹。 2. 在需要使用H5lock.js的组件中,通过import语句将H5lock.js引入进来…...
target采退、测评养号购物下单操作教程
1.点击右上角的Create account注册账号 2.填写账号信息 3. 进入自己需要购买的商品页面 点击pick it up购买 4. 进入购物车页面选择快递方式和地址后点击 check out按钮 5. 之后会提示绑定XYK,这里我是用虚拟XYK开卡平台进行支付的. 6. 确认订单无误后点击Place you…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
