在 Android 上测试 Kotlin 协程
文章目录
- 官方文档
- 在测试中调用挂起函数
- TestDispatchers
- StandardTestDispatcher
- UnconfinedTestDispatcher
- 注入测试调度程序
- 设置主调度程序
- 在测试之外创建调度程序
- 创建您自己的 TestScope
- 注入作用域
官方文档
https://developer.android.google.cn/kotlin/coroutines/test?hl=zh-cn
API 是 kotlinx.coroutines.test 库的一部分。如需访问这些 API,请务必添加相应工件作为项目的测试依赖项。
dependencies {testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
}
在测试中调用挂起函数
如需在测试中调用挂起函数,您必须位于协程中。由于 JUnit 测试函数本身并不是挂起函数,因此您需要在测试中调用协程构建器以启动新的协程。
suspend fun fetchData(): String {delay(1000L)return "Hello world"
}@Test
fun dataShouldBeHelloWorld() = runTest {val data = fetchData()assertEquals("Hello world", data)
}
TestDispatchers
TestDispatchers 是用于测试的 CoroutineDispatcher 实现。如果要在测试期间创建新的协程,您需要使用 TestDispatchers,以使新协程的执行可预测。
注意:新协程可直接在测试主体中创建,也可在测试中所调用的任何代码中创建(例如在测试的对象中)。
TestDispatcher 有两种可用的实现:StandardTestDispatcher 和 UnconfinedTestDispatcher,可分别对新启动的协程执行不同的调度。两者都使用 TestCoroutineScheduler 来控制虚拟时间并管理测试中正在运行的协程。
一个测试中只能使用一个调度器实例,且所有 TestDispatchers 应共用该调度器。如需了解如何共用调度器,请参阅注入测试调度程序。
为了启动顶级测试协程,runTest 会创建一个 TestScope,它是 CoroutineScope 的实现,将始终使用 TestDispatcher。如果未指定,TestScope 将默认创建 StandardTestDispatcher,并将其用于运行顶级测试协程。
StandardTestDispatcher
@Test
fun standardTest() = runTest {val userRepo = UserRepository()launch { userRepo.register("Alice") }launch { userRepo.register("Bob") }assertEquals(listOf("Alice", "Bob"), userRepo.getAllUsers()) // ❌ Fails
}
可通过多种方式让出测试协程,以让排队的协程运行。所有以下调用都可在返回之前让其他协程在测试线程上运行:
advanceUntilIdle:在调度器上运行所有其他协程,直到队列中没有任何内容。这是一个不错的默认选择,可让所有待处理的协程运行,适用于大多数测试场景。
advanceTimeBy:将虚拟时间提前指定时长,并运行已调度为在该虚拟时间点之前运行的所有协程。
runCurrent:运行已调度为在当前虚拟时间运行的协程。
UnconfinedTestDispatcher
如果在 UnconfinedTestDispatcher 上启动新协程,系统会在当前线程上快速启动。也就是说,这些协程会立即开始运行,而不会等待其协程构建器返回。在许多情况下,这种调度行为会使测试代码更加简单,因为您无需手动让出测试线程即可让新协程运行。
@Test
fun unconfinedTest() = runTest(UnconfinedTestDispatcher()) {val userRepo = UserRepository()launch { userRepo.register("Alice") }launch { userRepo.register("Bob") }assertEquals(listOf("Alice", "Bob"), userRepo.getAllUsers()) // ✅ Passes
}
注入测试调度程序
设置主调度程序
在本地单元测试中,封装 Android 界面线程的 Main 调度程序将无法使用,因为这些测试是在本地 JVM 而不是 Android 设备上执行的。如果被测试代码引用主线程,它会在单元测试期间抛出异常。
在测试之外创建调度程序
class Repository(private val ioDispatcher: CoroutineDispatcher) { /* ... */ }class RepositoryTestWithRule {private val repository = Repository(/* What TestDispatcher? */)@get:Ruleval mainDispatcherRule = MainDispatcherRule()@Testfun someRepositoryTest() = runTest {// Test the repository...// ...}
}
创建您自己的 TestScope
class SimpleExampleTest {val testScope = TestScope() // Creates a StandardTestDispatcher@Testfun someTest() = testScope.runTest {// ...}
}
注入作用域
如果有类创建需要您在测试期间控制的协程,则可以将协程作用域注入到该类中,并在测试中将其替换为 TestScope
class UserState(private val userRepository: UserRepository,private val scope: CoroutineScope,
) {private val _users = MutableStateFlow(emptyList<String>())val users: StateFlow<List<String>> = _users.asStateFlow()fun registerUser(name: String) {scope.launch {userRepository.register(name)_users.update { userRepository.getAllUsers() }}}
}
class UserStateTest {@Testfun addUserTest() = runTest { // this: TestScopeval repository = FakeUserRepository()val userState = UserState(repository, scope = this)userState.registerUser("Mona")advanceUntilIdle() // Let the coroutine complete and changes propagateassertEquals(listOf("Mona"), userState.users.value)}
}
相关文章:
在 Android 上测试 Kotlin 协程
文章目录 官方文档在测试中调用挂起函数TestDispatchersStandardTestDispatcherUnconfinedTestDispatcher 注入测试调度程序设置主调度程序在测试之外创建调度程序创建您自己的 TestScope注入作用域 官方文档 https://developer.android.google.cn/kotlin/coroutines/test?hl…...
图论04-【无权无向】-图的广度优先遍历BFS
文章目录 1. 代码仓库2. 广度优先遍历图解3.主要代码4. 完整代码 1. 代码仓库 https://github.com/Chufeng-Jiang/Graph-Theory 2. 广度优先遍历图解 3.主要代码 原点入队列原点出队列的同时,将与其相邻的顶点全部入队列下一个顶点出队列出队列的同时,将…...
vue3 v-model的使用
🙂博主:锅盖哒 🙂文章核心:vue3 v-model的使用 目录 前言 什么是v-model? 基本的v-model用法 自定义组件中的v-model 前言 当涉及到Vue.js 3的前端开发时,v-model是一个不可或缺的工具,它…...
Ubuntu 20.04 安装 Docker
大家好,我叫徐锦桐,个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家来访。 介绍 Docker容器具有以下三大特点: 轻量化:一台主机上运行的多个Dock…...
vue el-dialog弹出框自定义指令实现拖拽改变位置-宽度-高度
前言 在实际开发中我们经常使用el-dialog弹出框做表单,一般情况都是居中。遮挡到了一部分数据 当我们想要查看弹出框下面的数据时,就只能先把弹出框关闭,查看完数据之后在打开弹框 我们通过动态样式,和鼠标事件就可以实现。但自…...
玄铁C906——物理内存保护(PMP)介绍
1、前言 (1)本文描述的是玄铁C906的物理内存保护机制的实现中,与RISC-V架构手册中完整PMP机制的差异部分; (2)RISC-V架构的PMP机制,参考博客:《RISC-V架构——物理内存属性和物理内存…...
【进阶C语言】编译与链接、预处理符号详解
目录 一、翻译环境 编译 1.预编译(预处理) 2.编译 3.汇编 链接 二、运行环境 三、预处理符号详解 1.预定义符号 2.#define 3.#undef 4..命令行定义 5.条件编译 6.头文件包含 代码是怎么变成可执行程序的? 一、翻译环境 翻译环境…...
spring.profiles生效顺序
服务在不同环境启动,需要的运行参数可能会有差异,不同启动环境也可能公用同一份运行参,为了方便对这些不同环境相同和差异参数进行管理,springboot提供了文件配置化形式对这些参数进行管理,对于不同环境的差异化参数使…...
【经典PageRank 】02/2 算法和线性代数
系列前文:【经典 PageRank 】01/2 PageRank的基本原理-CSDN博客 一、说明 并非所有连接都同样重要! 该算法由 Sergey 和 Lawrence 开发,用于在 Google 搜索中对网页进行排名。基本原则是重要或值得信赖的网页更有可能链接到其他重要网页。例…...
【微客云】91优惠话费充值API接口开发功能介绍
话费充值接口文档 接口版本:1.0 ―、引言 文档概述 本文档提供话费充值接口规范说明,提供一整套的完整的接入示例(http 接口)供商户参 考,可以帮助商户开发人员快速完成接口开发与联调,实现与话费充值系统的交易互联。 公司官网…...
Kubernetes - 一键安装部署 K8S(附:Kubernetes Dashboard)
问题描述 不知道大伙是如何安装 K8s,特别还是集群的时候,我上一次安装搭建的时候,那个恶心到我了,真的是一步一个脚印走完整个搭建流程,爬了不少坑。 于是,才有了今天的文章,到底有没有可以一…...
Camera2开发基础知识篇——手机影像参数
1. 2、对焦 对焦指相机将图像清晰聚焦的过程。自动对焦功能可自动调整焦点,确保主体清晰锐利。手动对焦功能允许用户手动选择焦点。 3、焦距 简单理解就是指镜头的视角和放大倍数。实际到物理设备,焦距就是从镜片光学中心到底片、CCD或CMOS等成像平面…...
Unity之ShaderGraph如何实现无贴图水球效果
前言 我们今天来实现一个无贴图水球效果,如下图所示: 主要节点 UVSplit:可以获得UV在RGB三个颜色分别的分量 Remap:重映射节点 基于输入 In 值在输入In Min Max的 x 和 y 分量之间的线性插值,返回输入Out Min Max…...
【C语言】指针错题(类型分析)
题目: #include <stdio.h> int main () {int*p NULL;int arr[10] {0}; return 0; } 选项: A、p arr ; B、 int (* ptr )[10]& arr ; C、 p & arr [ 0 ]; D、 p & arr ; 解析: 1、 p 是一个指针变量,指…...
prosemirror 学习记录(二)创建 apple 节点
apple type 向 schema 中添加 apple type const nodes {apple: {inline: true,attrs: {name: { default: "unknown" },},group: "inline",draggable: true,parseDOM: [{tag: "span[custom-node-typeapple]",getAttrs(dom) {return {name: dom…...
自然语言处理---迁移学习
fasttext介绍 作为NLP工程领域常用的工具包,fasttext有两大作用:进行文本分类、训练词向量。在保持较高精度的情况下,快速的进行训练和预测是fasttext的最大优势。fasttext优势的原因: fasttext工具包中内含的fasttext模型具有十分简单的网络…...
node 第十天 原生node封装一个简易的服务器
原生node封装一个简易的服务器, 把前面几天的知识揉和起来做一个服务器基础实现, 首页访问, 静态资源服务器, 特定接口封装, 404app.js 服务器入口文件 app.js node app.js即可启动服务器 const { start } require(./modules/server); start();require_modules.js 整合模块导…...
php实战案例记录(25)intval函数的用法
在PHP中,intval()函数用于将一个字符串转换为整数。它的语法如下: intval(string $value, int $base 10): int参数说明: $value:要转换的字符串。$base(可选):进制数,默认为10。如…...
laravel框架介绍(二) composer命令下载laravel报错
1.composer命令下载laravel报如下错 : curl error 18 while downloading https://repo.packagist.org/p2/symfony/uid.j son: transfer closed with 3808 bytes remaining to read,具体为 解决方案:执行以下命令切换镜像 >composer con…...
代码签名证书到期了怎么续费?
我们都知道代码签名证书最长期限可以申请3年,但有的首次申请也会申请1年,这种情况下证书到期了就意味着要重新办理,同样的实名验证步骤还需要再走一遍,尤其目前无论是哪种类型的代码签名证书都会有物理硬件,即使交钱实…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...
python打卡day49@浙大疏锦行
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 一、通道注意力模块复习 & CBAM实现 import torch import torch.nn as nnclass CBAM(nn.Module):def __init__…...
